popover column resizing on grids
[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  * Portions of this file are based on pieces of Yahoo User Interface Library
2609  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2610  * YUI licensed under the BSD License:
2611  * http://developer.yahoo.net/yui/license.txt
2612  * <script type="text/javascript">
2613  *
2614  */
2615
2616 (function() {
2617     /**
2618      * @class Roo.lib.Ajax
2619      *
2620      */
2621     Roo.lib.Ajax = {
2622         /**
2623          * @static 
2624          */
2625         request : function(method, uri, cb, data, options) {
2626             if(options){
2627                 var hs = options.headers;
2628                 if(hs){
2629                     for(var h in hs){
2630                         if(hs.hasOwnProperty(h)){
2631                             this.initHeader(h, hs[h], false);
2632                         }
2633                     }
2634                 }
2635                 if(options.xmlData){
2636                     this.initHeader('Content-Type', 'text/xml', false);
2637                     method = 'POST';
2638                     data = options.xmlData;
2639                 }
2640             }
2641
2642             return this.asyncRequest(method, uri, cb, data);
2643         },
2644
2645         serializeForm : function(form) {
2646             if(typeof form == 'string') {
2647                 form = (document.getElementById(form) || document.forms[form]);
2648             }
2649
2650             var el, name, val, disabled, data = '', hasSubmit = false;
2651             for (var i = 0; i < form.elements.length; i++) {
2652                 el = form.elements[i];
2653                 disabled = form.elements[i].disabled;
2654                 name = form.elements[i].name;
2655                 val = form.elements[i].value;
2656
2657                 if (!disabled && name){
2658                     switch (el.type)
2659                             {
2660                         case 'select-one':
2661                         case 'select-multiple':
2662                             for (var j = 0; j < el.options.length; j++) {
2663                                 if (el.options[j].selected) {
2664                                     if (Roo.isIE) {
2665                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2666                                     }
2667                                     else {
2668                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2669                                     }
2670                                 }
2671                             }
2672                             break;
2673                         case 'radio':
2674                         case 'checkbox':
2675                             if (el.checked) {
2676                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2677                             }
2678                             break;
2679                         case 'file':
2680
2681                         case undefined:
2682
2683                         case 'reset':
2684
2685                         case 'button':
2686
2687                             break;
2688                         case 'submit':
2689                             if(hasSubmit == false) {
2690                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2691                                 hasSubmit = true;
2692                             }
2693                             break;
2694                         default:
2695                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2696                             break;
2697                     }
2698                 }
2699             }
2700             data = data.substr(0, data.length - 1);
2701             return data;
2702         },
2703
2704         headers:{},
2705
2706         hasHeaders:false,
2707
2708         useDefaultHeader:true,
2709
2710         defaultPostHeader:'application/x-www-form-urlencoded',
2711
2712         useDefaultXhrHeader:true,
2713
2714         defaultXhrHeader:'XMLHttpRequest',
2715
2716         hasDefaultHeaders:true,
2717
2718         defaultHeaders:{},
2719
2720         poll:{},
2721
2722         timeout:{},
2723
2724         pollInterval:50,
2725
2726         transactionId:0,
2727
2728         setProgId:function(id)
2729         {
2730             this.activeX.unshift(id);
2731         },
2732
2733         setDefaultPostHeader:function(b)
2734         {
2735             this.useDefaultHeader = b;
2736         },
2737
2738         setDefaultXhrHeader:function(b)
2739         {
2740             this.useDefaultXhrHeader = b;
2741         },
2742
2743         setPollingInterval:function(i)
2744         {
2745             if (typeof i == 'number' && isFinite(i)) {
2746                 this.pollInterval = i;
2747             }
2748         },
2749
2750         createXhrObject:function(transactionId)
2751         {
2752             var obj,http;
2753             try
2754             {
2755
2756                 http = new XMLHttpRequest();
2757
2758                 obj = { conn:http, tId:transactionId };
2759             }
2760             catch(e)
2761             {
2762                 for (var i = 0; i < this.activeX.length; ++i) {
2763                     try
2764                     {
2765
2766                         http = new ActiveXObject(this.activeX[i]);
2767
2768                         obj = { conn:http, tId:transactionId };
2769                         break;
2770                     }
2771                     catch(e) {
2772                     }
2773                 }
2774             }
2775             finally
2776             {
2777                 return obj;
2778             }
2779         },
2780
2781         getConnectionObject:function()
2782         {
2783             var o;
2784             var tId = this.transactionId;
2785
2786             try
2787             {
2788                 o = this.createXhrObject(tId);
2789                 if (o) {
2790                     this.transactionId++;
2791                 }
2792             }
2793             catch(e) {
2794             }
2795             finally
2796             {
2797                 return o;
2798             }
2799         },
2800
2801         asyncRequest:function(method, uri, callback, postData)
2802         {
2803             var o = this.getConnectionObject();
2804
2805             if (!o) {
2806                 return null;
2807             }
2808             else {
2809                 o.conn.open(method, uri, true);
2810
2811                 if (this.useDefaultXhrHeader) {
2812                     if (!this.defaultHeaders['X-Requested-With']) {
2813                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2814                     }
2815                 }
2816
2817                 if(postData && this.useDefaultHeader){
2818                     this.initHeader('Content-Type', this.defaultPostHeader);
2819                 }
2820
2821                  if (this.hasDefaultHeaders || this.hasHeaders) {
2822                     this.setHeader(o);
2823                 }
2824
2825                 this.handleReadyState(o, callback);
2826                 o.conn.send(postData || null);
2827
2828                 return o;
2829             }
2830         },
2831
2832         handleReadyState:function(o, callback)
2833         {
2834             var oConn = this;
2835
2836             if (callback && callback.timeout) {
2837                 
2838                 this.timeout[o.tId] = window.setTimeout(function() {
2839                     oConn.abort(o, callback, true);
2840                 }, callback.timeout);
2841             }
2842
2843             this.poll[o.tId] = window.setInterval(
2844                     function() {
2845                         if (o.conn && o.conn.readyState == 4) {
2846                             window.clearInterval(oConn.poll[o.tId]);
2847                             delete oConn.poll[o.tId];
2848
2849                             if(callback && callback.timeout) {
2850                                 window.clearTimeout(oConn.timeout[o.tId]);
2851                                 delete oConn.timeout[o.tId];
2852                             }
2853
2854                             oConn.handleTransactionResponse(o, callback);
2855                         }
2856                     }
2857                     , this.pollInterval);
2858         },
2859
2860         handleTransactionResponse:function(o, callback, isAbort)
2861         {
2862
2863             if (!callback) {
2864                 this.releaseObject(o);
2865                 return;
2866             }
2867
2868             var httpStatus, responseObject;
2869
2870             try
2871             {
2872                 if (o.conn.status !== undefined && o.conn.status != 0) {
2873                     httpStatus = o.conn.status;
2874                 }
2875                 else {
2876                     httpStatus = 13030;
2877                 }
2878             }
2879             catch(e) {
2880
2881
2882                 httpStatus = 13030;
2883             }
2884
2885             if (httpStatus >= 200 && httpStatus < 300) {
2886                 responseObject = this.createResponseObject(o, callback.argument);
2887                 if (callback.success) {
2888                     if (!callback.scope) {
2889                         callback.success(responseObject);
2890                     }
2891                     else {
2892
2893
2894                         callback.success.apply(callback.scope, [responseObject]);
2895                     }
2896                 }
2897             }
2898             else {
2899                 switch (httpStatus) {
2900
2901                     case 12002:
2902                     case 12029:
2903                     case 12030:
2904                     case 12031:
2905                     case 12152:
2906                     case 13030:
2907                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2908                         if (callback.failure) {
2909                             if (!callback.scope) {
2910                                 callback.failure(responseObject);
2911                             }
2912                             else {
2913                                 callback.failure.apply(callback.scope, [responseObject]);
2914                             }
2915                         }
2916                         break;
2917                     default:
2918                         responseObject = this.createResponseObject(o, callback.argument);
2919                         if (callback.failure) {
2920                             if (!callback.scope) {
2921                                 callback.failure(responseObject);
2922                             }
2923                             else {
2924                                 callback.failure.apply(callback.scope, [responseObject]);
2925                             }
2926                         }
2927                 }
2928             }
2929
2930             this.releaseObject(o);
2931             responseObject = null;
2932         },
2933
2934         createResponseObject:function(o, callbackArg)
2935         {
2936             var obj = {};
2937             var headerObj = {};
2938
2939             try
2940             {
2941                 var headerStr = o.conn.getAllResponseHeaders();
2942                 var header = headerStr.split('\n');
2943                 for (var i = 0; i < header.length; i++) {
2944                     var delimitPos = header[i].indexOf(':');
2945                     if (delimitPos != -1) {
2946                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2947                     }
2948                 }
2949             }
2950             catch(e) {
2951             }
2952
2953             obj.tId = o.tId;
2954             obj.status = o.conn.status;
2955             obj.statusText = o.conn.statusText;
2956             obj.getResponseHeader = headerObj;
2957             obj.getAllResponseHeaders = headerStr;
2958             obj.responseText = o.conn.responseText;
2959             obj.responseXML = o.conn.responseXML;
2960
2961             if (typeof callbackArg !== undefined) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         createExceptionObject:function(tId, callbackArg, isAbort)
2969         {
2970             var COMM_CODE = 0;
2971             var COMM_ERROR = 'communication failure';
2972             var ABORT_CODE = -1;
2973             var ABORT_ERROR = 'transaction aborted';
2974
2975             var obj = {};
2976
2977             obj.tId = tId;
2978             if (isAbort) {
2979                 obj.status = ABORT_CODE;
2980                 obj.statusText = ABORT_ERROR;
2981             }
2982             else {
2983                 obj.status = COMM_CODE;
2984                 obj.statusText = COMM_ERROR;
2985             }
2986
2987             if (callbackArg) {
2988                 obj.argument = callbackArg;
2989             }
2990
2991             return obj;
2992         },
2993
2994         initHeader:function(label, value, isDefault)
2995         {
2996             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2997
2998             if (headerObj[label] === undefined) {
2999                 headerObj[label] = value;
3000             }
3001             else {
3002
3003
3004                 headerObj[label] = value + "," + headerObj[label];
3005             }
3006
3007             if (isDefault) {
3008                 this.hasDefaultHeaders = true;
3009             }
3010             else {
3011                 this.hasHeaders = true;
3012             }
3013         },
3014
3015
3016         setHeader:function(o)
3017         {
3018             if (this.hasDefaultHeaders) {
3019                 for (var prop in this.defaultHeaders) {
3020                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3021                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3022                     }
3023                 }
3024             }
3025
3026             if (this.hasHeaders) {
3027                 for (var prop in this.headers) {
3028                     if (this.headers.hasOwnProperty(prop)) {
3029                         o.conn.setRequestHeader(prop, this.headers[prop]);
3030                     }
3031                 }
3032                 this.headers = {};
3033                 this.hasHeaders = false;
3034             }
3035         },
3036
3037         resetDefaultHeaders:function() {
3038             delete this.defaultHeaders;
3039             this.defaultHeaders = {};
3040             this.hasDefaultHeaders = false;
3041         },
3042
3043         abort:function(o, callback, isTimeout)
3044         {
3045             if(this.isCallInProgress(o)) {
3046                 o.conn.abort();
3047                 window.clearInterval(this.poll[o.tId]);
3048                 delete this.poll[o.tId];
3049                 if (isTimeout) {
3050                     delete this.timeout[o.tId];
3051                 }
3052
3053                 this.handleTransactionResponse(o, callback, true);
3054
3055                 return true;
3056             }
3057             else {
3058                 return false;
3059             }
3060         },
3061
3062
3063         isCallInProgress:function(o)
3064         {
3065             if (o && o.conn) {
3066                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3067             }
3068             else {
3069
3070                 return false;
3071             }
3072         },
3073
3074
3075         releaseObject:function(o)
3076         {
3077
3078             o.conn = null;
3079
3080             o = null;
3081         },
3082
3083         activeX:[
3084         'MSXML2.XMLHTTP.3.0',
3085         'MSXML2.XMLHTTP',
3086         'Microsoft.XMLHTTP'
3087         ]
3088
3089
3090     };
3091 })();/*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099
3100 Roo.lib.Region = function(t, r, b, l) {
3101     this.top = t;
3102     this[1] = t;
3103     this.right = r;
3104     this.bottom = b;
3105     this.left = l;
3106     this[0] = l;
3107 };
3108
3109
3110 Roo.lib.Region.prototype = {
3111     contains : function(region) {
3112         return ( region.left >= this.left &&
3113                  region.right <= this.right &&
3114                  region.top >= this.top &&
3115                  region.bottom <= this.bottom    );
3116
3117     },
3118
3119     getArea : function() {
3120         return ( (this.bottom - this.top) * (this.right - this.left) );
3121     },
3122
3123     intersect : function(region) {
3124         var t = Math.max(this.top, region.top);
3125         var r = Math.min(this.right, region.right);
3126         var b = Math.min(this.bottom, region.bottom);
3127         var l = Math.max(this.left, region.left);
3128
3129         if (b >= t && r >= l) {
3130             return new Roo.lib.Region(t, r, b, l);
3131         } else {
3132             return null;
3133         }
3134     },
3135     union : function(region) {
3136         var t = Math.min(this.top, region.top);
3137         var r = Math.max(this.right, region.right);
3138         var b = Math.max(this.bottom, region.bottom);
3139         var l = Math.min(this.left, region.left);
3140
3141         return new Roo.lib.Region(t, r, b, l);
3142     },
3143
3144     adjust : function(t, l, b, r) {
3145         this.top += t;
3146         this.left += l;
3147         this.right += r;
3148         this.bottom += b;
3149         return this;
3150     }
3151 };
3152
3153 Roo.lib.Region.getRegion = function(el) {
3154     var p = Roo.lib.Dom.getXY(el);
3155
3156     var t = p[1];
3157     var r = p[0] + el.offsetWidth;
3158     var b = p[1] + el.offsetHeight;
3159     var l = p[0];
3160
3161     return new Roo.lib.Region(t, r, b, l);
3162 };
3163 /*
3164  * Portions of this file are based on pieces of Yahoo User Interface Library
3165  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3166  * YUI licensed under the BSD License:
3167  * http://developer.yahoo.net/yui/license.txt
3168  * <script type="text/javascript">
3169  *
3170  */
3171 //@@dep Roo.lib.Region
3172
3173
3174 Roo.lib.Point = function(x, y) {
3175     if (x instanceof Array) {
3176         y = x[1];
3177         x = x[0];
3178     }
3179     this.x = this.right = this.left = this[0] = x;
3180     this.y = this.top = this.bottom = this[1] = y;
3181 };
3182
3183 Roo.lib.Point.prototype = new Roo.lib.Region();
3184 /*
3185  * Portions of this file are based on pieces of Yahoo User Interface Library
3186  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3187  * YUI licensed under the BSD License:
3188  * http://developer.yahoo.net/yui/license.txt
3189  * <script type="text/javascript">
3190  *
3191  */
3192  
3193 (function() {   
3194
3195     Roo.lib.Anim = {
3196         scroll : function(el, args, duration, easing, cb, scope) {
3197             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3198         },
3199
3200         motion : function(el, args, duration, easing, cb, scope) {
3201             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3202         },
3203
3204         color : function(el, args, duration, easing, cb, scope) {
3205             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3206         },
3207
3208         run : function(el, args, duration, easing, cb, scope, type) {
3209             type = type || Roo.lib.AnimBase;
3210             if (typeof easing == "string") {
3211                 easing = Roo.lib.Easing[easing];
3212             }
3213             var anim = new type(el, args, duration, easing);
3214             anim.animateX(function() {
3215                 Roo.callback(cb, scope);
3216             });
3217             return anim;
3218         }
3219     };
3220 })();/*
3221  * Portions of this file are based on pieces of Yahoo User Interface Library
3222  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3223  * YUI licensed under the BSD License:
3224  * http://developer.yahoo.net/yui/license.txt
3225  * <script type="text/javascript">
3226  *
3227  */
3228
3229 (function() {    
3230     var libFlyweight;
3231     
3232     function fly(el) {
3233         if (!libFlyweight) {
3234             libFlyweight = new Roo.Element.Flyweight();
3235         }
3236         libFlyweight.dom = el;
3237         return libFlyweight;
3238     }
3239
3240     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3241     
3242    
3243     
3244     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3245         if (el) {
3246             this.init(el, attributes, duration, method);
3247         }
3248     };
3249
3250     Roo.lib.AnimBase.fly = fly;
3251     
3252     
3253     
3254     Roo.lib.AnimBase.prototype = {
3255
3256         toString: function() {
3257             var el = this.getEl();
3258             var id = el.id || el.tagName;
3259             return ("Anim " + id);
3260         },
3261
3262         patterns: {
3263             noNegatives:        /width|height|opacity|padding/i,
3264             offsetAttribute:  /^((width|height)|(top|left))$/,
3265             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3266             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3267         },
3268
3269
3270         doMethod: function(attr, start, end) {
3271             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3272         },
3273
3274
3275         setAttribute: function(attr, val, unit) {
3276             if (this.patterns.noNegatives.test(attr)) {
3277                 val = (val > 0) ? val : 0;
3278             }
3279
3280             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3281         },
3282
3283
3284         getAttribute: function(attr) {
3285             var el = this.getEl();
3286             var val = fly(el).getStyle(attr);
3287
3288             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3289                 return parseFloat(val);
3290             }
3291
3292             var a = this.patterns.offsetAttribute.exec(attr) || [];
3293             var pos = !!( a[3] );
3294             var box = !!( a[2] );
3295
3296
3297             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3298                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3299             } else {
3300                 val = 0;
3301             }
3302
3303             return val;
3304         },
3305
3306
3307         getDefaultUnit: function(attr) {
3308             if (this.patterns.defaultUnit.test(attr)) {
3309                 return 'px';
3310             }
3311
3312             return '';
3313         },
3314
3315         animateX : function(callback, scope) {
3316             var f = function() {
3317                 this.onComplete.removeListener(f);
3318                 if (typeof callback == "function") {
3319                     callback.call(scope || this, this);
3320                 }
3321             };
3322             this.onComplete.addListener(f, this);
3323             this.animate();
3324         },
3325
3326
3327         setRuntimeAttribute: function(attr) {
3328             var start;
3329             var end;
3330             var attributes = this.attributes;
3331
3332             this.runtimeAttributes[attr] = {};
3333
3334             var isset = function(prop) {
3335                 return (typeof prop !== 'undefined');
3336             };
3337
3338             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3339                 return false;
3340             }
3341
3342             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3343
3344
3345             if (isset(attributes[attr]['to'])) {
3346                 end = attributes[attr]['to'];
3347             } else if (isset(attributes[attr]['by'])) {
3348                 if (start.constructor == Array) {
3349                     end = [];
3350                     for (var i = 0, len = start.length; i < len; ++i) {
3351                         end[i] = start[i] + attributes[attr]['by'][i];
3352                     }
3353                 } else {
3354                     end = start + attributes[attr]['by'];
3355                 }
3356             }
3357
3358             this.runtimeAttributes[attr].start = start;
3359             this.runtimeAttributes[attr].end = end;
3360
3361
3362             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3363         },
3364
3365
3366         init: function(el, attributes, duration, method) {
3367
3368             var isAnimated = false;
3369
3370
3371             var startTime = null;
3372
3373
3374             var actualFrames = 0;
3375
3376
3377             el = Roo.getDom(el);
3378
3379
3380             this.attributes = attributes || {};
3381
3382
3383             this.duration = duration || 1;
3384
3385
3386             this.method = method || Roo.lib.Easing.easeNone;
3387
3388
3389             this.useSeconds = true;
3390
3391
3392             this.currentFrame = 0;
3393
3394
3395             this.totalFrames = Roo.lib.AnimMgr.fps;
3396
3397
3398             this.getEl = function() {
3399                 return el;
3400             };
3401
3402
3403             this.isAnimated = function() {
3404                 return isAnimated;
3405             };
3406
3407
3408             this.getStartTime = function() {
3409                 return startTime;
3410             };
3411
3412             this.runtimeAttributes = {};
3413
3414
3415             this.animate = function() {
3416                 if (this.isAnimated()) {
3417                     return false;
3418                 }
3419
3420                 this.currentFrame = 0;
3421
3422                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3423
3424                 Roo.lib.AnimMgr.registerElement(this);
3425             };
3426
3427
3428             this.stop = function(finish) {
3429                 if (finish) {
3430                     this.currentFrame = this.totalFrames;
3431                     this._onTween.fire();
3432                 }
3433                 Roo.lib.AnimMgr.stop(this);
3434             };
3435
3436             var onStart = function() {
3437                 this.onStart.fire();
3438
3439                 this.runtimeAttributes = {};
3440                 for (var attr in this.attributes) {
3441                     this.setRuntimeAttribute(attr);
3442                 }
3443
3444                 isAnimated = true;
3445                 actualFrames = 0;
3446                 startTime = new Date();
3447             };
3448
3449
3450             var onTween = function() {
3451                 var data = {
3452                     duration: new Date() - this.getStartTime(),
3453                     currentFrame: this.currentFrame
3454                 };
3455
3456                 data.toString = function() {
3457                     return (
3458                             'duration: ' + data.duration +
3459                             ', currentFrame: ' + data.currentFrame
3460                             );
3461                 };
3462
3463                 this.onTween.fire(data);
3464
3465                 var runtimeAttributes = this.runtimeAttributes;
3466
3467                 for (var attr in runtimeAttributes) {
3468                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3469                 }
3470
3471                 actualFrames += 1;
3472             };
3473
3474             var onComplete = function() {
3475                 var actual_duration = (new Date() - startTime) / 1000 ;
3476
3477                 var data = {
3478                     duration: actual_duration,
3479                     frames: actualFrames,
3480                     fps: actualFrames / actual_duration
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', frames: ' + data.frames +
3487                             ', fps: ' + data.fps
3488                             );
3489                 };
3490
3491                 isAnimated = false;
3492                 actualFrames = 0;
3493                 this.onComplete.fire(data);
3494             };
3495
3496
3497             this._onStart = new Roo.util.Event(this);
3498             this.onStart = new Roo.util.Event(this);
3499             this.onTween = new Roo.util.Event(this);
3500             this._onTween = new Roo.util.Event(this);
3501             this.onComplete = new Roo.util.Event(this);
3502             this._onComplete = new Roo.util.Event(this);
3503             this._onStart.addListener(onStart);
3504             this._onTween.addListener(onTween);
3505             this._onComplete.addListener(onComplete);
3506         }
3507     };
3508 })();
3509 /*
3510  * Portions of this file are based on pieces of Yahoo User Interface Library
3511  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3512  * YUI licensed under the BSD License:
3513  * http://developer.yahoo.net/yui/license.txt
3514  * <script type="text/javascript">
3515  *
3516  */
3517
3518 Roo.lib.AnimMgr = new function() {
3519
3520     var thread = null;
3521
3522
3523     var queue = [];
3524
3525
3526     var tweenCount = 0;
3527
3528
3529     this.fps = 1000;
3530
3531
3532     this.delay = 1;
3533
3534
3535     this.registerElement = function(tween) {
3536         queue[queue.length] = tween;
3537         tweenCount += 1;
3538         tween._onStart.fire();
3539         this.start();
3540     };
3541
3542
3543     this.unRegister = function(tween, index) {
3544         tween._onComplete.fire();
3545         index = index || getIndex(tween);
3546         if (index != -1) {
3547             queue.splice(index, 1);
3548         }
3549
3550         tweenCount -= 1;
3551         if (tweenCount <= 0) {
3552             this.stop();
3553         }
3554     };
3555
3556
3557     this.start = function() {
3558         if (thread === null) {
3559             thread = setInterval(this.run, this.delay);
3560         }
3561     };
3562
3563
3564     this.stop = function(tween) {
3565         if (!tween) {
3566             clearInterval(thread);
3567
3568             for (var i = 0, len = queue.length; i < len; ++i) {
3569                 if (queue[0].isAnimated()) {
3570                     this.unRegister(queue[0], 0);
3571                 }
3572             }
3573
3574             queue = [];
3575             thread = null;
3576             tweenCount = 0;
3577         }
3578         else {
3579             this.unRegister(tween);
3580         }
3581     };
3582
3583
3584     this.run = function() {
3585         for (var i = 0, len = queue.length; i < len; ++i) {
3586             var tween = queue[i];
3587             if (!tween || !tween.isAnimated()) {
3588                 continue;
3589             }
3590
3591             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3592             {
3593                 tween.currentFrame += 1;
3594
3595                 if (tween.useSeconds) {
3596                     correctFrame(tween);
3597                 }
3598                 tween._onTween.fire();
3599             }
3600             else {
3601                 Roo.lib.AnimMgr.stop(tween, i);
3602             }
3603         }
3604     };
3605
3606     var getIndex = function(anim) {
3607         for (var i = 0, len = queue.length; i < len; ++i) {
3608             if (queue[i] == anim) {
3609                 return i;
3610             }
3611         }
3612         return -1;
3613     };
3614
3615
3616     var correctFrame = function(tween) {
3617         var frames = tween.totalFrames;
3618         var frame = tween.currentFrame;
3619         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3620         var elapsed = (new Date() - tween.getStartTime());
3621         var tweak = 0;
3622
3623         if (elapsed < tween.duration * 1000) {
3624             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3625         } else {
3626             tweak = frames - (frame + 1);
3627         }
3628         if (tweak > 0 && isFinite(tweak)) {
3629             if (tween.currentFrame + tweak >= frames) {
3630                 tweak = frames - (frame + 1);
3631             }
3632
3633             tween.currentFrame += tweak;
3634         }
3635     };
3636 };
3637
3638     /*
3639  * Portions of this file are based on pieces of Yahoo User Interface Library
3640  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3641  * YUI licensed under the BSD License:
3642  * http://developer.yahoo.net/yui/license.txt
3643  * <script type="text/javascript">
3644  *
3645  */
3646 Roo.lib.Bezier = new function() {
3647
3648         this.getPosition = function(points, t) {
3649             var n = points.length;
3650             var tmp = [];
3651
3652             for (var i = 0; i < n; ++i) {
3653                 tmp[i] = [points[i][0], points[i][1]];
3654             }
3655
3656             for (var j = 1; j < n; ++j) {
3657                 for (i = 0; i < n - j; ++i) {
3658                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3659                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3660                 }
3661             }
3662
3663             return [ tmp[0][0], tmp[0][1] ];
3664
3665         };
3666     };/*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 (function() {
3675
3676     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3677         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3678     };
3679
3680     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3681
3682     var fly = Roo.lib.AnimBase.fly;
3683     var Y = Roo.lib;
3684     var superclass = Y.ColorAnim.superclass;
3685     var proto = Y.ColorAnim.prototype;
3686
3687     proto.toString = function() {
3688         var el = this.getEl();
3689         var id = el.id || el.tagName;
3690         return ("ColorAnim " + id);
3691     };
3692
3693     proto.patterns.color = /color$/i;
3694     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3695     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3696     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3697     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3698
3699
3700     proto.parseColor = function(s) {
3701         if (s.length == 3) {
3702             return s;
3703         }
3704
3705         var c = this.patterns.hex.exec(s);
3706         if (c && c.length == 4) {
3707             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3708         }
3709
3710         c = this.patterns.rgb.exec(s);
3711         if (c && c.length == 4) {
3712             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3713         }
3714
3715         c = this.patterns.hex3.exec(s);
3716         if (c && c.length == 4) {
3717             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3718         }
3719
3720         return null;
3721     };
3722     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3723     proto.getAttribute = function(attr) {
3724         var el = this.getEl();
3725         if (this.patterns.color.test(attr)) {
3726             var val = fly(el).getStyle(attr);
3727
3728             if (this.patterns.transparent.test(val)) {
3729                 var parent = el.parentNode;
3730                 val = fly(parent).getStyle(attr);
3731
3732                 while (parent && this.patterns.transparent.test(val)) {
3733                     parent = parent.parentNode;
3734                     val = fly(parent).getStyle(attr);
3735                     if (parent.tagName.toUpperCase() == 'HTML') {
3736                         val = '#fff';
3737                     }
3738                 }
3739             }
3740         } else {
3741             val = superclass.getAttribute.call(this, attr);
3742         }
3743
3744         return val;
3745     };
3746     proto.getAttribute = function(attr) {
3747         var el = this.getEl();
3748         if (this.patterns.color.test(attr)) {
3749             var val = fly(el).getStyle(attr);
3750
3751             if (this.patterns.transparent.test(val)) {
3752                 var parent = el.parentNode;
3753                 val = fly(parent).getStyle(attr);
3754
3755                 while (parent && this.patterns.transparent.test(val)) {
3756                     parent = parent.parentNode;
3757                     val = fly(parent).getStyle(attr);
3758                     if (parent.tagName.toUpperCase() == 'HTML') {
3759                         val = '#fff';
3760                     }
3761                 }
3762             }
3763         } else {
3764             val = superclass.getAttribute.call(this, attr);
3765         }
3766
3767         return val;
3768     };
3769
3770     proto.doMethod = function(attr, start, end) {
3771         var val;
3772
3773         if (this.patterns.color.test(attr)) {
3774             val = [];
3775             for (var i = 0, len = start.length; i < len; ++i) {
3776                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3777             }
3778
3779             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3780         }
3781         else {
3782             val = superclass.doMethod.call(this, attr, start, end);
3783         }
3784
3785         return val;
3786     };
3787
3788     proto.setRuntimeAttribute = function(attr) {
3789         superclass.setRuntimeAttribute.call(this, attr);
3790
3791         if (this.patterns.color.test(attr)) {
3792             var attributes = this.attributes;
3793             var start = this.parseColor(this.runtimeAttributes[attr].start);
3794             var end = this.parseColor(this.runtimeAttributes[attr].end);
3795
3796             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3797                 end = this.parseColor(attributes[attr].by);
3798
3799                 for (var i = 0, len = start.length; i < len; ++i) {
3800                     end[i] = start[i] + end[i];
3801                 }
3802             }
3803
3804             this.runtimeAttributes[attr].start = start;
3805             this.runtimeAttributes[attr].end = end;
3806         }
3807     };
3808 })();
3809
3810 /*
3811  * Portions of this file are based on pieces of Yahoo User Interface Library
3812  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3813  * YUI licensed under the BSD License:
3814  * http://developer.yahoo.net/yui/license.txt
3815  * <script type="text/javascript">
3816  *
3817  */
3818 Roo.lib.Easing = {
3819
3820
3821     easeNone: function (t, b, c, d) {
3822         return c * t / d + b;
3823     },
3824
3825
3826     easeIn: function (t, b, c, d) {
3827         return c * (t /= d) * t + b;
3828     },
3829
3830
3831     easeOut: function (t, b, c, d) {
3832         return -c * (t /= d) * (t - 2) + b;
3833     },
3834
3835
3836     easeBoth: function (t, b, c, d) {
3837         if ((t /= d / 2) < 1) {
3838             return c / 2 * t * t + b;
3839         }
3840
3841         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3842     },
3843
3844
3845     easeInStrong: function (t, b, c, d) {
3846         return c * (t /= d) * t * t * t + b;
3847     },
3848
3849
3850     easeOutStrong: function (t, b, c, d) {
3851         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3852     },
3853
3854
3855     easeBothStrong: function (t, b, c, d) {
3856         if ((t /= d / 2) < 1) {
3857             return c / 2 * t * t * t * t + b;
3858         }
3859
3860         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3861     },
3862
3863
3864
3865     elasticIn: function (t, b, c, d, a, p) {
3866         if (t == 0) {
3867             return b;
3868         }
3869         if ((t /= d) == 1) {
3870             return b + c;
3871         }
3872         if (!p) {
3873             p = d * .3;
3874         }
3875
3876         if (!a || a < Math.abs(c)) {
3877             a = c;
3878             var s = p / 4;
3879         }
3880         else {
3881             var s = p / (2 * Math.PI) * Math.asin(c / a);
3882         }
3883
3884         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3885     },
3886
3887
3888     elasticOut: function (t, b, c, d, a, p) {
3889         if (t == 0) {
3890             return b;
3891         }
3892         if ((t /= d) == 1) {
3893             return b + c;
3894         }
3895         if (!p) {
3896             p = d * .3;
3897         }
3898
3899         if (!a || a < Math.abs(c)) {
3900             a = c;
3901             var s = p / 4;
3902         }
3903         else {
3904             var s = p / (2 * Math.PI) * Math.asin(c / a);
3905         }
3906
3907         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3908     },
3909
3910
3911     elasticBoth: function (t, b, c, d, a, p) {
3912         if (t == 0) {
3913             return b;
3914         }
3915
3916         if ((t /= d / 2) == 2) {
3917             return b + c;
3918         }
3919
3920         if (!p) {
3921             p = d * (.3 * 1.5);
3922         }
3923
3924         if (!a || a < Math.abs(c)) {
3925             a = c;
3926             var s = p / 4;
3927         }
3928         else {
3929             var s = p / (2 * Math.PI) * Math.asin(c / a);
3930         }
3931
3932         if (t < 1) {
3933             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3934                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3935         }
3936         return a * Math.pow(2, -10 * (t -= 1)) *
3937                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3938     },
3939
3940
3941
3942     backIn: function (t, b, c, d, s) {
3943         if (typeof s == 'undefined') {
3944             s = 1.70158;
3945         }
3946         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3947     },
3948
3949
3950     backOut: function (t, b, c, d, s) {
3951         if (typeof s == 'undefined') {
3952             s = 1.70158;
3953         }
3954         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3955     },
3956
3957
3958     backBoth: function (t, b, c, d, s) {
3959         if (typeof s == 'undefined') {
3960             s = 1.70158;
3961         }
3962
3963         if ((t /= d / 2 ) < 1) {
3964             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3965         }
3966         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3967     },
3968
3969
3970     bounceIn: function (t, b, c, d) {
3971         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3972     },
3973
3974
3975     bounceOut: function (t, b, c, d) {
3976         if ((t /= d) < (1 / 2.75)) {
3977             return c * (7.5625 * t * t) + b;
3978         } else if (t < (2 / 2.75)) {
3979             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3980         } else if (t < (2.5 / 2.75)) {
3981             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3982         }
3983         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3984     },
3985
3986
3987     bounceBoth: function (t, b, c, d) {
3988         if (t < d / 2) {
3989             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3990         }
3991         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3992     }
3993 };/*
3994  * Portions of this file are based on pieces of Yahoo User Interface Library
3995  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3996  * YUI licensed under the BSD License:
3997  * http://developer.yahoo.net/yui/license.txt
3998  * <script type="text/javascript">
3999  *
4000  */
4001     (function() {
4002         Roo.lib.Motion = function(el, attributes, duration, method) {
4003             if (el) {
4004                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4005             }
4006         };
4007
4008         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4009
4010
4011         var Y = Roo.lib;
4012         var superclass = Y.Motion.superclass;
4013         var proto = Y.Motion.prototype;
4014
4015         proto.toString = function() {
4016             var el = this.getEl();
4017             var id = el.id || el.tagName;
4018             return ("Motion " + id);
4019         };
4020
4021         proto.patterns.points = /^points$/i;
4022
4023         proto.setAttribute = function(attr, val, unit) {
4024             if (this.patterns.points.test(attr)) {
4025                 unit = unit || 'px';
4026                 superclass.setAttribute.call(this, 'left', val[0], unit);
4027                 superclass.setAttribute.call(this, 'top', val[1], unit);
4028             } else {
4029                 superclass.setAttribute.call(this, attr, val, unit);
4030             }
4031         };
4032
4033         proto.getAttribute = function(attr) {
4034             if (this.patterns.points.test(attr)) {
4035                 var val = [
4036                         superclass.getAttribute.call(this, 'left'),
4037                         superclass.getAttribute.call(this, 'top')
4038                         ];
4039             } else {
4040                 val = superclass.getAttribute.call(this, attr);
4041             }
4042
4043             return val;
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (this.patterns.points.test(attr)) {
4050                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4051                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4052             } else {
4053                 val = superclass.doMethod.call(this, attr, start, end);
4054             }
4055             return val;
4056         };
4057
4058         proto.setRuntimeAttribute = function(attr) {
4059             if (this.patterns.points.test(attr)) {
4060                 var el = this.getEl();
4061                 var attributes = this.attributes;
4062                 var start;
4063                 var control = attributes['points']['control'] || [];
4064                 var end;
4065                 var i, len;
4066
4067                 if (control.length > 0 && !(control[0] instanceof Array)) {
4068                     control = [control];
4069                 } else {
4070                     var tmp = [];
4071                     for (i = 0,len = control.length; i < len; ++i) {
4072                         tmp[i] = control[i];
4073                     }
4074                     control = tmp;
4075                 }
4076
4077                 Roo.fly(el).position();
4078
4079                 if (isset(attributes['points']['from'])) {
4080                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4081                 }
4082                 else {
4083                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4084                 }
4085
4086                 start = this.getAttribute('points');
4087
4088
4089                 if (isset(attributes['points']['to'])) {
4090                     end = translateValues.call(this, attributes['points']['to'], start);
4091
4092                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4093                     for (i = 0,len = control.length; i < len; ++i) {
4094                         control[i] = translateValues.call(this, control[i], start);
4095                     }
4096
4097
4098                 } else if (isset(attributes['points']['by'])) {
4099                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4100
4101                     for (i = 0,len = control.length; i < len; ++i) {
4102                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4103                     }
4104                 }
4105
4106                 this.runtimeAttributes[attr] = [start];
4107
4108                 if (control.length > 0) {
4109                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4110                 }
4111
4112                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4113             }
4114             else {
4115                 superclass.setRuntimeAttribute.call(this, attr);
4116             }
4117         };
4118
4119         var translateValues = function(val, start) {
4120             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4121             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4122
4123             return val;
4124         };
4125
4126         var isset = function(prop) {
4127             return (typeof prop !== 'undefined');
4128         };
4129     })();
4130 /*
4131  * Portions of this file are based on pieces of Yahoo User Interface Library
4132  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4133  * YUI licensed under the BSD License:
4134  * http://developer.yahoo.net/yui/license.txt
4135  * <script type="text/javascript">
4136  *
4137  */
4138     (function() {
4139         Roo.lib.Scroll = function(el, attributes, duration, method) {
4140             if (el) {
4141                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4142             }
4143         };
4144
4145         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4146
4147
4148         var Y = Roo.lib;
4149         var superclass = Y.Scroll.superclass;
4150         var proto = Y.Scroll.prototype;
4151
4152         proto.toString = function() {
4153             var el = this.getEl();
4154             var id = el.id || el.tagName;
4155             return ("Scroll " + id);
4156         };
4157
4158         proto.doMethod = function(attr, start, end) {
4159             var val = null;
4160
4161             if (attr == 'scroll') {
4162                 val = [
4163                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4164                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4165                         ];
4166
4167             } else {
4168                 val = superclass.doMethod.call(this, attr, start, end);
4169             }
4170             return val;
4171         };
4172
4173         proto.getAttribute = function(attr) {
4174             var val = null;
4175             var el = this.getEl();
4176
4177             if (attr == 'scroll') {
4178                 val = [ el.scrollLeft, el.scrollTop ];
4179             } else {
4180                 val = superclass.getAttribute.call(this, attr);
4181             }
4182
4183             return val;
4184         };
4185
4186         proto.setAttribute = function(attr, val, unit) {
4187             var el = this.getEl();
4188
4189             if (attr == 'scroll') {
4190                 el.scrollLeft = val[0];
4191                 el.scrollTop = val[1];
4192             } else {
4193                 superclass.setAttribute.call(this, attr, val, unit);
4194             }
4195         };
4196     })();
4197 /*
4198  * Based on:
4199  * Ext JS Library 1.1.1
4200  * Copyright(c) 2006-2007, Ext JS, LLC.
4201  *
4202  * Originally Released Under LGPL - original licence link has changed is not relivant.
4203  *
4204  * Fork - LGPL
4205  * <script type="text/javascript">
4206  */
4207
4208
4209 // nasty IE9 hack - what a pile of crap that is..
4210
4211  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4212     Range.prototype.createContextualFragment = function (html) {
4213         var doc = window.document;
4214         var container = doc.createElement("div");
4215         container.innerHTML = html;
4216         var frag = doc.createDocumentFragment(), n;
4217         while ((n = container.firstChild)) {
4218             frag.appendChild(n);
4219         }
4220         return frag;
4221     };
4222 }
4223
4224 /**
4225  * @class Roo.DomHelper
4226  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4227  * 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>.
4228  * @singleton
4229  */
4230 Roo.DomHelper = function(){
4231     var tempTableEl = null;
4232     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4233     var tableRe = /^table|tbody|tr|td$/i;
4234     var xmlns = {};
4235     // build as innerHTML where available
4236     /** @ignore */
4237     var createHtml = function(o){
4238         if(typeof o == 'string'){
4239             return o;
4240         }
4241         var b = "";
4242         if(!o.tag){
4243             o.tag = "div";
4244         }
4245         b += "<" + o.tag;
4246         for(var attr in o){
4247             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4248             if(attr == "style"){
4249                 var s = o["style"];
4250                 if(typeof s == "function"){
4251                     s = s.call();
4252                 }
4253                 if(typeof s == "string"){
4254                     b += ' style="' + s + '"';
4255                 }else if(typeof s == "object"){
4256                     b += ' style="';
4257                     for(var key in s){
4258                         if(typeof s[key] != "function"){
4259                             b += key + ":" + s[key] + ";";
4260                         }
4261                     }
4262                     b += '"';
4263                 }
4264             }else{
4265                 if(attr == "cls"){
4266                     b += ' class="' + o["cls"] + '"';
4267                 }else if(attr == "htmlFor"){
4268                     b += ' for="' + o["htmlFor"] + '"';
4269                 }else{
4270                     b += " " + attr + '="' + o[attr] + '"';
4271                 }
4272             }
4273         }
4274         if(emptyTags.test(o.tag)){
4275             b += "/>";
4276         }else{
4277             b += ">";
4278             var cn = o.children || o.cn;
4279             if(cn){
4280                 //http://bugs.kde.org/show_bug.cgi?id=71506
4281                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4282                     for(var i = 0, len = cn.length; i < len; i++) {
4283                         b += createHtml(cn[i], b);
4284                     }
4285                 }else{
4286                     b += createHtml(cn, b);
4287                 }
4288             }
4289             if(o.html){
4290                 b += o.html;
4291             }
4292             b += "</" + o.tag + ">";
4293         }
4294         return b;
4295     };
4296
4297     // build as dom
4298     /** @ignore */
4299     var createDom = function(o, parentNode){
4300          
4301         // defininition craeted..
4302         var ns = false;
4303         if (o.ns && o.ns != 'html') {
4304                
4305             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4306                 xmlns[o.ns] = o.xmlns;
4307                 ns = o.xmlns;
4308             }
4309             if (typeof(xmlns[o.ns]) == 'undefined') {
4310                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4311             }
4312             ns = xmlns[o.ns];
4313         }
4314         
4315         
4316         if (typeof(o) == 'string') {
4317             return parentNode.appendChild(document.createTextNode(o));
4318         }
4319         o.tag = o.tag || div;
4320         if (o.ns && Roo.isIE) {
4321             ns = false;
4322             o.tag = o.ns + ':' + o.tag;
4323             
4324         }
4325         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4326         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4327         for(var attr in o){
4328             
4329             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4330                     attr == "style" || typeof o[attr] == "function") { continue; }
4331                     
4332             if(attr=="cls" && Roo.isIE){
4333                 el.className = o["cls"];
4334             }else{
4335                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4336                 else { 
4337                     el[attr] = o[attr];
4338                 }
4339             }
4340         }
4341         Roo.DomHelper.applyStyles(el, o.style);
4342         var cn = o.children || o.cn;
4343         if(cn){
4344             //http://bugs.kde.org/show_bug.cgi?id=71506
4345              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4346                 for(var i = 0, len = cn.length; i < len; i++) {
4347                     createDom(cn[i], el);
4348                 }
4349             }else{
4350                 createDom(cn, el);
4351             }
4352         }
4353         if(o.html){
4354             el.innerHTML = o.html;
4355         }
4356         if(parentNode){
4357            parentNode.appendChild(el);
4358         }
4359         return el;
4360     };
4361
4362     var ieTable = function(depth, s, h, e){
4363         tempTableEl.innerHTML = [s, h, e].join('');
4364         var i = -1, el = tempTableEl;
4365         while(++i < depth && el.firstChild){
4366             el = el.firstChild;
4367         }
4368         return el;
4369     };
4370
4371     // kill repeat to save bytes
4372     var ts = '<table>',
4373         te = '</table>',
4374         tbs = ts+'<tbody>',
4375         tbe = '</tbody>'+te,
4376         trs = tbs + '<tr>',
4377         tre = '</tr>'+tbe;
4378
4379     /**
4380      * @ignore
4381      * Nasty code for IE's broken table implementation
4382      */
4383     var insertIntoTable = function(tag, where, el, html){
4384         if(!tempTableEl){
4385             tempTableEl = document.createElement('div');
4386         }
4387         var node;
4388         var before = null;
4389         if(tag == 'td'){
4390             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4391                 return;
4392             }
4393             if(where == 'beforebegin'){
4394                 before = el;
4395                 el = el.parentNode;
4396             } else{
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399             }
4400             node = ieTable(4, trs, html, tre);
4401         }
4402         else if(tag == 'tr'){
4403             if(where == 'beforebegin'){
4404                 before = el;
4405                 el = el.parentNode;
4406                 node = ieTable(3, tbs, html, tbe);
4407             } else if(where == 'afterend'){
4408                 before = el.nextSibling;
4409                 el = el.parentNode;
4410                 node = ieTable(3, tbs, html, tbe);
4411             } else{ // INTO a TR
4412                 if(where == 'afterbegin'){
4413                     before = el.firstChild;
4414                 }
4415                 node = ieTable(4, trs, html, tre);
4416             }
4417         } else if(tag == 'tbody'){
4418             if(where == 'beforebegin'){
4419                 before = el;
4420                 el = el.parentNode;
4421                 node = ieTable(2, ts, html, te);
4422             } else if(where == 'afterend'){
4423                 before = el.nextSibling;
4424                 el = el.parentNode;
4425                 node = ieTable(2, ts, html, te);
4426             } else{
4427                 if(where == 'afterbegin'){
4428                     before = el.firstChild;
4429                 }
4430                 node = ieTable(3, tbs, html, tbe);
4431             }
4432         } else{ // TABLE
4433             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4434                 return;
4435             }
4436             if(where == 'afterbegin'){
4437                 before = el.firstChild;
4438             }
4439             node = ieTable(2, ts, html, te);
4440         }
4441         el.insertBefore(node, before);
4442         return node;
4443     };
4444
4445     return {
4446     /** True to force the use of DOM instead of html fragments @type Boolean */
4447     useDom : false,
4448
4449     /**
4450      * Returns the markup for the passed Element(s) config
4451      * @param {Object} o The Dom object spec (and children)
4452      * @return {String}
4453      */
4454     markup : function(o){
4455         return createHtml(o);
4456     },
4457
4458     /**
4459      * Applies a style specification to an element
4460      * @param {String/HTMLElement} el The element to apply styles to
4461      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4462      * a function which returns such a specification.
4463      */
4464     applyStyles : function(el, styles){
4465         if(styles){
4466            el = Roo.fly(el);
4467            if(typeof styles == "string"){
4468                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4469                var matches;
4470                while ((matches = re.exec(styles)) != null){
4471                    el.setStyle(matches[1], matches[2]);
4472                }
4473            }else if (typeof styles == "object"){
4474                for (var style in styles){
4475                   el.setStyle(style, styles[style]);
4476                }
4477            }else if (typeof styles == "function"){
4478                 Roo.DomHelper.applyStyles(el, styles.call());
4479            }
4480         }
4481     },
4482
4483     /**
4484      * Inserts an HTML fragment into the Dom
4485      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4486      * @param {HTMLElement} el The context element
4487      * @param {String} html The HTML fragmenet
4488      * @return {HTMLElement} The new node
4489      */
4490     insertHtml : function(where, el, html){
4491         where = where.toLowerCase();
4492         if(el.insertAdjacentHTML){
4493             if(tableRe.test(el.tagName)){
4494                 var rs;
4495                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4496                     return rs;
4497                 }
4498             }
4499             switch(where){
4500                 case "beforebegin":
4501                     el.insertAdjacentHTML('BeforeBegin', html);
4502                     return el.previousSibling;
4503                 case "afterbegin":
4504                     el.insertAdjacentHTML('AfterBegin', html);
4505                     return el.firstChild;
4506                 case "beforeend":
4507                     el.insertAdjacentHTML('BeforeEnd', html);
4508                     return el.lastChild;
4509                 case "afterend":
4510                     el.insertAdjacentHTML('AfterEnd', html);
4511                     return el.nextSibling;
4512             }
4513             throw 'Illegal insertion point -> "' + where + '"';
4514         }
4515         var range = el.ownerDocument.createRange();
4516         var frag;
4517         switch(where){
4518              case "beforebegin":
4519                 range.setStartBefore(el);
4520                 frag = range.createContextualFragment(html);
4521                 el.parentNode.insertBefore(frag, el);
4522                 return el.previousSibling;
4523              case "afterbegin":
4524                 if(el.firstChild){
4525                     range.setStartBefore(el.firstChild);
4526                     frag = range.createContextualFragment(html);
4527                     el.insertBefore(frag, el.firstChild);
4528                     return el.firstChild;
4529                 }else{
4530                     el.innerHTML = html;
4531                     return el.firstChild;
4532                 }
4533             case "beforeend":
4534                 if(el.lastChild){
4535                     range.setStartAfter(el.lastChild);
4536                     frag = range.createContextualFragment(html);
4537                     el.appendChild(frag);
4538                     return el.lastChild;
4539                 }else{
4540                     el.innerHTML = html;
4541                     return el.lastChild;
4542                 }
4543             case "afterend":
4544                 range.setStartAfter(el);
4545                 frag = range.createContextualFragment(html);
4546                 el.parentNode.insertBefore(frag, el.nextSibling);
4547                 return el.nextSibling;
4548             }
4549             throw 'Illegal insertion point -> "' + where + '"';
4550     },
4551
4552     /**
4553      * Creates new Dom element(s) and inserts them before el
4554      * @param {String/HTMLElement/Element} el The context element
4555      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4556      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4557      * @return {HTMLElement/Roo.Element} The new node
4558      */
4559     insertBefore : function(el, o, returnElement){
4560         return this.doInsert(el, o, returnElement, "beforeBegin");
4561     },
4562
4563     /**
4564      * Creates new Dom element(s) and inserts them after el
4565      * @param {String/HTMLElement/Element} el The context element
4566      * @param {Object} o The Dom object spec (and children)
4567      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4568      * @return {HTMLElement/Roo.Element} The new node
4569      */
4570     insertAfter : function(el, o, returnElement){
4571         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4572     },
4573
4574     /**
4575      * Creates new Dom element(s) and inserts them as the first child of el
4576      * @param {String/HTMLElement/Element} el The context element
4577      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4578      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4579      * @return {HTMLElement/Roo.Element} The new node
4580      */
4581     insertFirst : function(el, o, returnElement){
4582         return this.doInsert(el, o, returnElement, "afterBegin");
4583     },
4584
4585     // private
4586     doInsert : function(el, o, returnElement, pos, sibling){
4587         el = Roo.getDom(el);
4588         var newNode;
4589         if(this.useDom || o.ns){
4590             newNode = createDom(o, null);
4591             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4592         }else{
4593             var html = createHtml(o);
4594             newNode = this.insertHtml(pos, el, html);
4595         }
4596         return returnElement ? Roo.get(newNode, true) : newNode;
4597     },
4598
4599     /**
4600      * Creates new Dom element(s) and appends them to el
4601      * @param {String/HTMLElement/Element} el The context element
4602      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4603      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4604      * @return {HTMLElement/Roo.Element} The new node
4605      */
4606     append : function(el, o, returnElement){
4607         el = Roo.getDom(el);
4608         var newNode;
4609         if(this.useDom || o.ns){
4610             newNode = createDom(o, null);
4611             el.appendChild(newNode);
4612         }else{
4613             var html = createHtml(o);
4614             newNode = this.insertHtml("beforeEnd", el, html);
4615         }
4616         return returnElement ? Roo.get(newNode, true) : newNode;
4617     },
4618
4619     /**
4620      * Creates new Dom element(s) and overwrites the contents of el with them
4621      * @param {String/HTMLElement/Element} el The context element
4622      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4623      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4624      * @return {HTMLElement/Roo.Element} The new node
4625      */
4626     overwrite : function(el, o, returnElement){
4627         el = Roo.getDom(el);
4628         if (o.ns) {
4629           
4630             while (el.childNodes.length) {
4631                 el.removeChild(el.firstChild);
4632             }
4633             createDom(o, el);
4634         } else {
4635             el.innerHTML = createHtml(o);   
4636         }
4637         
4638         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4639     },
4640
4641     /**
4642      * Creates a new Roo.DomHelper.Template from the Dom object spec
4643      * @param {Object} o The Dom object spec (and children)
4644      * @return {Roo.DomHelper.Template} The new template
4645      */
4646     createTemplate : function(o){
4647         var html = createHtml(o);
4648         return new Roo.Template(html);
4649     }
4650     };
4651 }();
4652 /*
4653  * Based on:
4654  * Ext JS Library 1.1.1
4655  * Copyright(c) 2006-2007, Ext JS, LLC.
4656  *
4657  * Originally Released Under LGPL - original licence link has changed is not relivant.
4658  *
4659  * Fork - LGPL
4660  * <script type="text/javascript">
4661  */
4662  
4663 /**
4664 * @class Roo.Template
4665 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4666 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4667 * Usage:
4668 <pre><code>
4669 var t = new Roo.Template({
4670     html :  '&lt;div name="{id}"&gt;' + 
4671         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4672         '&lt;/div&gt;',
4673     myformat: function (value, allValues) {
4674         return 'XX' + value;
4675     }
4676 });
4677 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4678 </code></pre>
4679 * For more information see this blog post with examples:
4680 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4681      - Create Elements using DOM, HTML fragments and Templates</a>. 
4682 * @constructor
4683 * @param {Object} cfg - Configuration object.
4684 */
4685 Roo.Template = function(cfg){
4686     // BC!
4687     if(cfg instanceof Array){
4688         cfg = cfg.join("");
4689     }else if(arguments.length > 1){
4690         cfg = Array.prototype.join.call(arguments, "");
4691     }
4692     
4693     
4694     if (typeof(cfg) == 'object') {
4695         Roo.apply(this,cfg)
4696     } else {
4697         // bc
4698         this.html = cfg;
4699     }
4700     if (this.url) {
4701         this.load();
4702     }
4703     
4704 };
4705 Roo.Template.prototype = {
4706     
4707     /**
4708      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4709      */
4710     onLoad : false,
4711     
4712     
4713     /**
4714      * @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..
4715      *                    it should be fixed so that template is observable...
4716      */
4717     url : false,
4718     /**
4719      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4720      */
4721     html : '',
4722     
4723     
4724     compiled : false,
4725     loaded : false,
4726     /**
4727      * Returns an HTML fragment of this template with the specified values applied.
4728      * @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'})
4729      * @return {String} The HTML fragment
4730      */
4731     
4732    
4733     
4734     applyTemplate : function(values){
4735         //Roo.log(["applyTemplate", values]);
4736         try {
4737            
4738             if(this.compiled){
4739                 return this.compiled(values);
4740             }
4741             var useF = this.disableFormats !== true;
4742             var fm = Roo.util.Format, tpl = this;
4743             var fn = function(m, name, format, args){
4744                 if(format && useF){
4745                     if(format.substr(0, 5) == "this."){
4746                         return tpl.call(format.substr(5), values[name], values);
4747                     }else{
4748                         if(args){
4749                             // quoted values are required for strings in compiled templates, 
4750                             // but for non compiled we need to strip them
4751                             // quoted reversed for jsmin
4752                             var re = /^\s*['"](.*)["']\s*$/;
4753                             args = args.split(',');
4754                             for(var i = 0, len = args.length; i < len; i++){
4755                                 args[i] = args[i].replace(re, "$1");
4756                             }
4757                             args = [values[name]].concat(args);
4758                         }else{
4759                             args = [values[name]];
4760                         }
4761                         return fm[format].apply(fm, args);
4762                     }
4763                 }else{
4764                     return values[name] !== undefined ? values[name] : "";
4765                 }
4766             };
4767             return this.html.replace(this.re, fn);
4768         } catch (e) {
4769             Roo.log(e);
4770             throw e;
4771         }
4772          
4773     },
4774     
4775     loading : false,
4776       
4777     load : function ()
4778     {
4779          
4780         if (this.loading) {
4781             return;
4782         }
4783         var _t = this;
4784         
4785         this.loading = true;
4786         this.compiled = false;
4787         
4788         var cx = new Roo.data.Connection();
4789         cx.request({
4790             url : this.url,
4791             method : 'GET',
4792             success : function (response) {
4793                 _t.loading = false;
4794                 _t.url = false;
4795                 
4796                 _t.set(response.responseText,true);
4797                 _t.loaded = true;
4798                 if (_t.onLoad) {
4799                     _t.onLoad();
4800                 }
4801              },
4802             failure : function(response) {
4803                 Roo.log("Template failed to load from " + _t.url);
4804                 _t.loading = false;
4805             }
4806         });
4807     },
4808
4809     /**
4810      * Sets the HTML used as the template and optionally compiles it.
4811      * @param {String} html
4812      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4813      * @return {Roo.Template} this
4814      */
4815     set : function(html, compile){
4816         this.html = html;
4817         this.compiled = false;
4818         if(compile){
4819             this.compile();
4820         }
4821         return this;
4822     },
4823     
4824     /**
4825      * True to disable format functions (defaults to false)
4826      * @type Boolean
4827      */
4828     disableFormats : false,
4829     
4830     /**
4831     * The regular expression used to match template variables 
4832     * @type RegExp
4833     * @property 
4834     */
4835     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4836     
4837     /**
4838      * Compiles the template into an internal function, eliminating the RegEx overhead.
4839      * @return {Roo.Template} this
4840      */
4841     compile : function(){
4842         var fm = Roo.util.Format;
4843         var useF = this.disableFormats !== true;
4844         var sep = Roo.isGecko ? "+" : ",";
4845         var fn = function(m, name, format, args){
4846             if(format && useF){
4847                 args = args ? ',' + args : "";
4848                 if(format.substr(0, 5) != "this."){
4849                     format = "fm." + format + '(';
4850                 }else{
4851                     format = 'this.call("'+ format.substr(5) + '", ';
4852                     args = ", values";
4853                 }
4854             }else{
4855                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4856             }
4857             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4858         };
4859         var body;
4860         // branched to use + in gecko and [].join() in others
4861         if(Roo.isGecko){
4862             body = "this.compiled = function(values){ return '" +
4863                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4864                     "';};";
4865         }else{
4866             body = ["this.compiled = function(values){ return ['"];
4867             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4868             body.push("'].join('');};");
4869             body = body.join('');
4870         }
4871         /**
4872          * eval:var:values
4873          * eval:var:fm
4874          */
4875         eval(body);
4876         return this;
4877     },
4878     
4879     // private function used to call members
4880     call : function(fnName, value, allValues){
4881         return this[fnName](value, allValues);
4882     },
4883     
4884     /**
4885      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4886      * @param {String/HTMLElement/Roo.Element} el The context element
4887      * @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'})
4888      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4889      * @return {HTMLElement/Roo.Element} The new node or Element
4890      */
4891     insertFirst: function(el, values, returnElement){
4892         return this.doInsert('afterBegin', el, values, returnElement);
4893     },
4894
4895     /**
4896      * Applies the supplied values to the template and inserts the new node(s) before el.
4897      * @param {String/HTMLElement/Roo.Element} el The context element
4898      * @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'})
4899      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4900      * @return {HTMLElement/Roo.Element} The new node or Element
4901      */
4902     insertBefore: function(el, values, returnElement){
4903         return this.doInsert('beforeBegin', el, values, returnElement);
4904     },
4905
4906     /**
4907      * Applies the supplied values to the template and inserts the new node(s) after el.
4908      * @param {String/HTMLElement/Roo.Element} el The context element
4909      * @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'})
4910      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4911      * @return {HTMLElement/Roo.Element} The new node or Element
4912      */
4913     insertAfter : function(el, values, returnElement){
4914         return this.doInsert('afterEnd', el, values, returnElement);
4915     },
4916     
4917     /**
4918      * Applies the supplied values to the template and appends the new node(s) to el.
4919      * @param {String/HTMLElement/Roo.Element} el The context element
4920      * @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'})
4921      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4922      * @return {HTMLElement/Roo.Element} The new node or Element
4923      */
4924     append : function(el, values, returnElement){
4925         return this.doInsert('beforeEnd', el, values, returnElement);
4926     },
4927
4928     doInsert : function(where, el, values, returnEl){
4929         el = Roo.getDom(el);
4930         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4931         return returnEl ? Roo.get(newNode, true) : newNode;
4932     },
4933
4934     /**
4935      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4936      * @param {String/HTMLElement/Roo.Element} el The context element
4937      * @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'})
4938      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4939      * @return {HTMLElement/Roo.Element} The new node or Element
4940      */
4941     overwrite : function(el, values, returnElement){
4942         el = Roo.getDom(el);
4943         el.innerHTML = this.applyTemplate(values);
4944         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4945     }
4946 };
4947 /**
4948  * Alias for {@link #applyTemplate}
4949  * @method
4950  */
4951 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4952
4953 // backwards compat
4954 Roo.DomHelper.Template = Roo.Template;
4955
4956 /**
4957  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4958  * @param {String/HTMLElement} el A DOM element or its id
4959  * @returns {Roo.Template} The created template
4960  * @static
4961  */
4962 Roo.Template.from = function(el){
4963     el = Roo.getDom(el);
4964     return new Roo.Template(el.value || el.innerHTML);
4965 };/*
4966  * Based on:
4967  * Ext JS Library 1.1.1
4968  * Copyright(c) 2006-2007, Ext JS, LLC.
4969  *
4970  * Originally Released Under LGPL - original licence link has changed is not relivant.
4971  *
4972  * Fork - LGPL
4973  * <script type="text/javascript">
4974  */
4975  
4976
4977 /*
4978  * This is code is also distributed under MIT license for use
4979  * with jQuery and prototype JavaScript libraries.
4980  */
4981 /**
4982  * @class Roo.DomQuery
4983 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).
4984 <p>
4985 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>
4986
4987 <p>
4988 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.
4989 </p>
4990 <h4>Element Selectors:</h4>
4991 <ul class="list">
4992     <li> <b>*</b> any element</li>
4993     <li> <b>E</b> an element with the tag E</li>
4994     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4995     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4996     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4997     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4998 </ul>
4999 <h4>Attribute Selectors:</h4>
5000 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5001 <ul class="list">
5002     <li> <b>E[foo]</b> has an attribute "foo"</li>
5003     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5004     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5005     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5006     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5007     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5008     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5009 </ul>
5010 <h4>Pseudo Classes:</h4>
5011 <ul class="list">
5012     <li> <b>E:first-child</b> E is the first child of its parent</li>
5013     <li> <b>E:last-child</b> E is the last child of its parent</li>
5014     <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>
5015     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5016     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5017     <li> <b>E:only-child</b> E is the only child of its parent</li>
5018     <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>
5019     <li> <b>E:first</b> the first E in the resultset</li>
5020     <li> <b>E:last</b> the last E in the resultset</li>
5021     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5022     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5023     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5024     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5025     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5026     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5027     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5028     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5029     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5030 </ul>
5031 <h4>CSS Value Selectors:</h4>
5032 <ul class="list">
5033     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5034     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5035     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5036     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5037     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5038     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5039 </ul>
5040  * @singleton
5041  */
5042 Roo.DomQuery = function(){
5043     var cache = {}, simpleCache = {}, valueCache = {};
5044     var nonSpace = /\S/;
5045     var trimRe = /^\s+|\s+$/g;
5046     var tplRe = /\{(\d+)\}/g;
5047     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5048     var tagTokenRe = /^(#)?([\w-\*]+)/;
5049     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5050
5051     function child(p, index){
5052         var i = 0;
5053         var n = p.firstChild;
5054         while(n){
5055             if(n.nodeType == 1){
5056                if(++i == index){
5057                    return n;
5058                }
5059             }
5060             n = n.nextSibling;
5061         }
5062         return null;
5063     };
5064
5065     function next(n){
5066         while((n = n.nextSibling) && n.nodeType != 1);
5067         return n;
5068     };
5069
5070     function prev(n){
5071         while((n = n.previousSibling) && n.nodeType != 1);
5072         return n;
5073     };
5074
5075     function children(d){
5076         var n = d.firstChild, ni = -1;
5077             while(n){
5078                 var nx = n.nextSibling;
5079                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5080                     d.removeChild(n);
5081                 }else{
5082                     n.nodeIndex = ++ni;
5083                 }
5084                 n = nx;
5085             }
5086             return this;
5087         };
5088
5089     function byClassName(c, a, v){
5090         if(!v){
5091             return c;
5092         }
5093         var r = [], ri = -1, cn;
5094         for(var i = 0, ci; ci = c[i]; i++){
5095             
5096             
5097             if((' '+
5098                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5099                  +' ').indexOf(v) != -1){
5100                 r[++ri] = ci;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function attrValue(n, attr){
5107         if(!n.tagName && typeof n.length != "undefined"){
5108             n = n[0];
5109         }
5110         if(!n){
5111             return null;
5112         }
5113         if(attr == "for"){
5114             return n.htmlFor;
5115         }
5116         if(attr == "class" || attr == "className"){
5117             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5118         }
5119         return n.getAttribute(attr) || n[attr];
5120
5121     };
5122
5123     function getNodes(ns, mode, tagName){
5124         var result = [], ri = -1, cs;
5125         if(!ns){
5126             return result;
5127         }
5128         tagName = tagName || "*";
5129         if(typeof ns.getElementsByTagName != "undefined"){
5130             ns = [ns];
5131         }
5132         if(!mode){
5133             for(var i = 0, ni; ni = ns[i]; i++){
5134                 cs = ni.getElementsByTagName(tagName);
5135                 for(var j = 0, ci; ci = cs[j]; j++){
5136                     result[++ri] = ci;
5137                 }
5138             }
5139         }else if(mode == "/" || mode == ">"){
5140             var utag = tagName.toUpperCase();
5141             for(var i = 0, ni, cn; ni = ns[i]; i++){
5142                 cn = ni.children || ni.childNodes;
5143                 for(var j = 0, cj; cj = cn[j]; j++){
5144                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5145                         result[++ri] = cj;
5146                     }
5147                 }
5148             }
5149         }else if(mode == "+"){
5150             var utag = tagName.toUpperCase();
5151             for(var i = 0, n; n = ns[i]; i++){
5152                 while((n = n.nextSibling) && n.nodeType != 1);
5153                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5154                     result[++ri] = n;
5155                 }
5156             }
5157         }else if(mode == "~"){
5158             for(var i = 0, n; n = ns[i]; i++){
5159                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5160                 if(n){
5161                     result[++ri] = n;
5162                 }
5163             }
5164         }
5165         return result;
5166     };
5167
5168     function concat(a, b){
5169         if(b.slice){
5170             return a.concat(b);
5171         }
5172         for(var i = 0, l = b.length; i < l; i++){
5173             a[a.length] = b[i];
5174         }
5175         return a;
5176     }
5177
5178     function byTag(cs, tagName){
5179         if(cs.tagName || cs == document){
5180             cs = [cs];
5181         }
5182         if(!tagName){
5183             return cs;
5184         }
5185         var r = [], ri = -1;
5186         tagName = tagName.toLowerCase();
5187         for(var i = 0, ci; ci = cs[i]; i++){
5188             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5189                 r[++ri] = ci;
5190             }
5191         }
5192         return r;
5193     };
5194
5195     function byId(cs, attr, id){
5196         if(cs.tagName || cs == document){
5197             cs = [cs];
5198         }
5199         if(!id){
5200             return cs;
5201         }
5202         var r = [], ri = -1;
5203         for(var i = 0,ci; ci = cs[i]; i++){
5204             if(ci && ci.id == id){
5205                 r[++ri] = ci;
5206                 return r;
5207             }
5208         }
5209         return r;
5210     };
5211
5212     function byAttribute(cs, attr, value, op, custom){
5213         var r = [], ri = -1, st = custom=="{";
5214         var f = Roo.DomQuery.operators[op];
5215         for(var i = 0, ci; ci = cs[i]; i++){
5216             var a;
5217             if(st){
5218                 a = Roo.DomQuery.getStyle(ci, attr);
5219             }
5220             else if(attr == "class" || attr == "className"){
5221                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5222             }else if(attr == "for"){
5223                 a = ci.htmlFor;
5224             }else if(attr == "href"){
5225                 a = ci.getAttribute("href", 2);
5226             }else{
5227                 a = ci.getAttribute(attr);
5228             }
5229             if((f && f(a, value)) || (!f && a)){
5230                 r[++ri] = ci;
5231             }
5232         }
5233         return r;
5234     };
5235
5236     function byPseudo(cs, name, value){
5237         return Roo.DomQuery.pseudos[name](cs, value);
5238     };
5239
5240     // This is for IE MSXML which does not support expandos.
5241     // IE runs the same speed using setAttribute, however FF slows way down
5242     // and Safari completely fails so they need to continue to use expandos.
5243     var isIE = window.ActiveXObject ? true : false;
5244
5245     // this eval is stop the compressor from
5246     // renaming the variable to something shorter
5247     
5248     /** eval:var:batch */
5249     var batch = 30803; 
5250
5251     var key = 30803;
5252
5253     function nodupIEXml(cs){
5254         var d = ++key;
5255         cs[0].setAttribute("_nodup", d);
5256         var r = [cs[0]];
5257         for(var i = 1, len = cs.length; i < len; i++){
5258             var c = cs[i];
5259             if(!c.getAttribute("_nodup") != d){
5260                 c.setAttribute("_nodup", d);
5261                 r[r.length] = c;
5262             }
5263         }
5264         for(var i = 0, len = cs.length; i < len; i++){
5265             cs[i].removeAttribute("_nodup");
5266         }
5267         return r;
5268     }
5269
5270     function nodup(cs){
5271         if(!cs){
5272             return [];
5273         }
5274         var len = cs.length, c, i, r = cs, cj, ri = -1;
5275         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5276             return cs;
5277         }
5278         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5279             return nodupIEXml(cs);
5280         }
5281         var d = ++key;
5282         cs[0]._nodup = d;
5283         for(i = 1; c = cs[i]; i++){
5284             if(c._nodup != d){
5285                 c._nodup = d;
5286             }else{
5287                 r = [];
5288                 for(var j = 0; j < i; j++){
5289                     r[++ri] = cs[j];
5290                 }
5291                 for(j = i+1; cj = cs[j]; j++){
5292                     if(cj._nodup != d){
5293                         cj._nodup = d;
5294                         r[++ri] = cj;
5295                     }
5296                 }
5297                 return r;
5298             }
5299         }
5300         return r;
5301     }
5302
5303     function quickDiffIEXml(c1, c2){
5304         var d = ++key;
5305         for(var i = 0, len = c1.length; i < len; i++){
5306             c1[i].setAttribute("_qdiff", d);
5307         }
5308         var r = [];
5309         for(var i = 0, len = c2.length; i < len; i++){
5310             if(c2[i].getAttribute("_qdiff") != d){
5311                 r[r.length] = c2[i];
5312             }
5313         }
5314         for(var i = 0, len = c1.length; i < len; i++){
5315            c1[i].removeAttribute("_qdiff");
5316         }
5317         return r;
5318     }
5319
5320     function quickDiff(c1, c2){
5321         var len1 = c1.length;
5322         if(!len1){
5323             return c2;
5324         }
5325         if(isIE && c1[0].selectSingleNode){
5326             return quickDiffIEXml(c1, c2);
5327         }
5328         var d = ++key;
5329         for(var i = 0; i < len1; i++){
5330             c1[i]._qdiff = d;
5331         }
5332         var r = [];
5333         for(var i = 0, len = c2.length; i < len; i++){
5334             if(c2[i]._qdiff != d){
5335                 r[r.length] = c2[i];
5336             }
5337         }
5338         return r;
5339     }
5340
5341     function quickId(ns, mode, root, id){
5342         if(ns == root){
5343            var d = root.ownerDocument || root;
5344            return d.getElementById(id);
5345         }
5346         ns = getNodes(ns, mode, "*");
5347         return byId(ns, null, id);
5348     }
5349
5350     return {
5351         getStyle : function(el, name){
5352             return Roo.fly(el).getStyle(name);
5353         },
5354         /**
5355          * Compiles a selector/xpath query into a reusable function. The returned function
5356          * takes one parameter "root" (optional), which is the context node from where the query should start.
5357          * @param {String} selector The selector/xpath query
5358          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5359          * @return {Function}
5360          */
5361         compile : function(path, type){
5362             type = type || "select";
5363             
5364             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5365             var q = path, mode, lq;
5366             var tk = Roo.DomQuery.matchers;
5367             var tklen = tk.length;
5368             var mm;
5369
5370             // accept leading mode switch
5371             var lmode = q.match(modeRe);
5372             if(lmode && lmode[1]){
5373                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5374                 q = q.replace(lmode[1], "");
5375             }
5376             // strip leading slashes
5377             while(path.substr(0, 1)=="/"){
5378                 path = path.substr(1);
5379             }
5380
5381             while(q && lq != q){
5382                 lq = q;
5383                 var tm = q.match(tagTokenRe);
5384                 if(type == "select"){
5385                     if(tm){
5386                         if(tm[1] == "#"){
5387                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5388                         }else{
5389                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5390                         }
5391                         q = q.replace(tm[0], "");
5392                     }else if(q.substr(0, 1) != '@'){
5393                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5394                     }
5395                 }else{
5396                     if(tm){
5397                         if(tm[1] == "#"){
5398                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5399                         }else{
5400                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5401                         }
5402                         q = q.replace(tm[0], "");
5403                     }
5404                 }
5405                 while(!(mm = q.match(modeRe))){
5406                     var matched = false;
5407                     for(var j = 0; j < tklen; j++){
5408                         var t = tk[j];
5409                         var m = q.match(t.re);
5410                         if(m){
5411                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5412                                                     return m[i];
5413                                                 });
5414                             q = q.replace(m[0], "");
5415                             matched = true;
5416                             break;
5417                         }
5418                     }
5419                     // prevent infinite loop on bad selector
5420                     if(!matched){
5421                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5422                     }
5423                 }
5424                 if(mm[1]){
5425                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5426                     q = q.replace(mm[1], "");
5427                 }
5428             }
5429             fn[fn.length] = "return nodup(n);\n}";
5430             
5431              /** 
5432               * list of variables that need from compression as they are used by eval.
5433              *  eval:var:batch 
5434              *  eval:var:nodup
5435              *  eval:var:byTag
5436              *  eval:var:ById
5437              *  eval:var:getNodes
5438              *  eval:var:quickId
5439              *  eval:var:mode
5440              *  eval:var:root
5441              *  eval:var:n
5442              *  eval:var:byClassName
5443              *  eval:var:byPseudo
5444              *  eval:var:byAttribute
5445              *  eval:var:attrValue
5446              * 
5447              **/ 
5448             eval(fn.join(""));
5449             return f;
5450         },
5451
5452         /**
5453          * Selects a group of elements.
5454          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5455          * @param {Node} root (optional) The start of the query (defaults to document).
5456          * @return {Array}
5457          */
5458         select : function(path, root, type){
5459             if(!root || root == document){
5460                 root = document;
5461             }
5462             if(typeof root == "string"){
5463                 root = document.getElementById(root);
5464             }
5465             var paths = path.split(",");
5466             var results = [];
5467             for(var i = 0, len = paths.length; i < len; i++){
5468                 var p = paths[i].replace(trimRe, "");
5469                 if(!cache[p]){
5470                     cache[p] = Roo.DomQuery.compile(p);
5471                     if(!cache[p]){
5472                         throw p + " is not a valid selector";
5473                     }
5474                 }
5475                 var result = cache[p](root);
5476                 if(result && result != document){
5477                     results = results.concat(result);
5478                 }
5479             }
5480             if(paths.length > 1){
5481                 return nodup(results);
5482             }
5483             return results;
5484         },
5485
5486         /**
5487          * Selects a single element.
5488          * @param {String} selector The selector/xpath query
5489          * @param {Node} root (optional) The start of the query (defaults to document).
5490          * @return {Element}
5491          */
5492         selectNode : function(path, root){
5493             return Roo.DomQuery.select(path, root)[0];
5494         },
5495
5496         /**
5497          * Selects the value of a node, optionally replacing null with the defaultValue.
5498          * @param {String} selector The selector/xpath query
5499          * @param {Node} root (optional) The start of the query (defaults to document).
5500          * @param {String} defaultValue
5501          */
5502         selectValue : function(path, root, defaultValue){
5503             path = path.replace(trimRe, "");
5504             if(!valueCache[path]){
5505                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5506             }
5507             var n = valueCache[path](root);
5508             n = n[0] ? n[0] : n;
5509             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5510             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5511         },
5512
5513         /**
5514          * Selects the value of a node, parsing integers and floats.
5515          * @param {String} selector The selector/xpath query
5516          * @param {Node} root (optional) The start of the query (defaults to document).
5517          * @param {Number} defaultValue
5518          * @return {Number}
5519          */
5520         selectNumber : function(path, root, defaultValue){
5521             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5522             return parseFloat(v);
5523         },
5524
5525         /**
5526          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5527          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5528          * @param {String} selector The simple selector to test
5529          * @return {Boolean}
5530          */
5531         is : function(el, ss){
5532             if(typeof el == "string"){
5533                 el = document.getElementById(el);
5534             }
5535             var isArray = (el instanceof Array);
5536             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5537             return isArray ? (result.length == el.length) : (result.length > 0);
5538         },
5539
5540         /**
5541          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5542          * @param {Array} el An array of elements to filter
5543          * @param {String} selector The simple selector to test
5544          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5545          * the selector instead of the ones that match
5546          * @return {Array}
5547          */
5548         filter : function(els, ss, nonMatches){
5549             ss = ss.replace(trimRe, "");
5550             if(!simpleCache[ss]){
5551                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5552             }
5553             var result = simpleCache[ss](els);
5554             return nonMatches ? quickDiff(result, els) : result;
5555         },
5556
5557         /**
5558          * Collection of matching regular expressions and code snippets.
5559          */
5560         matchers : [{
5561                 re: /^\.([\w-]+)/,
5562                 select: 'n = byClassName(n, null, " {1} ");'
5563             }, {
5564                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5565                 select: 'n = byPseudo(n, "{1}", "{2}");'
5566             },{
5567                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5568                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5569             }, {
5570                 re: /^#([\w-]+)/,
5571                 select: 'n = byId(n, null, "{1}");'
5572             },{
5573                 re: /^@([\w-]+)/,
5574                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5575             }
5576         ],
5577
5578         /**
5579          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5580          * 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;.
5581          */
5582         operators : {
5583             "=" : function(a, v){
5584                 return a == v;
5585             },
5586             "!=" : function(a, v){
5587                 return a != v;
5588             },
5589             "^=" : function(a, v){
5590                 return a && a.substr(0, v.length) == v;
5591             },
5592             "$=" : function(a, v){
5593                 return a && a.substr(a.length-v.length) == v;
5594             },
5595             "*=" : function(a, v){
5596                 return a && a.indexOf(v) !== -1;
5597             },
5598             "%=" : function(a, v){
5599                 return (a % v) == 0;
5600             },
5601             "|=" : function(a, v){
5602                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5603             },
5604             "~=" : function(a, v){
5605                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5606             }
5607         },
5608
5609         /**
5610          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5611          * and the argument (if any) supplied in the selector.
5612          */
5613         pseudos : {
5614             "first-child" : function(c){
5615                 var r = [], ri = -1, n;
5616                 for(var i = 0, ci; ci = n = c[i]; i++){
5617                     while((n = n.previousSibling) && n.nodeType != 1);
5618                     if(!n){
5619                         r[++ri] = ci;
5620                     }
5621                 }
5622                 return r;
5623             },
5624
5625             "last-child" : function(c){
5626                 var r = [], ri = -1, n;
5627                 for(var i = 0, ci; ci = n = c[i]; i++){
5628                     while((n = n.nextSibling) && n.nodeType != 1);
5629                     if(!n){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "nth-child" : function(c, a) {
5637                 var r = [], ri = -1;
5638                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5639                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5640                 for(var i = 0, n; n = c[i]; i++){
5641                     var pn = n.parentNode;
5642                     if (batch != pn._batch) {
5643                         var j = 0;
5644                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5645                             if(cn.nodeType == 1){
5646                                cn.nodeIndex = ++j;
5647                             }
5648                         }
5649                         pn._batch = batch;
5650                     }
5651                     if (f == 1) {
5652                         if (l == 0 || n.nodeIndex == l){
5653                             r[++ri] = n;
5654                         }
5655                     } else if ((n.nodeIndex + l) % f == 0){
5656                         r[++ri] = n;
5657                     }
5658                 }
5659
5660                 return r;
5661             },
5662
5663             "only-child" : function(c){
5664                 var r = [], ri = -1;;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     if(!prev(ci) && !next(ci)){
5667                         r[++ri] = ci;
5668                     }
5669                 }
5670                 return r;
5671             },
5672
5673             "empty" : function(c){
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     var cns = ci.childNodes, j = 0, cn, empty = true;
5677                     while(cn = cns[j]){
5678                         ++j;
5679                         if(cn.nodeType == 1 || cn.nodeType == 3){
5680                             empty = false;
5681                             break;
5682                         }
5683                     }
5684                     if(empty){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "contains" : function(c, v){
5692                 var r = [], ri = -1;
5693                 for(var i = 0, ci; ci = c[i]; i++){
5694                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5695                         r[++ri] = ci;
5696                     }
5697                 }
5698                 return r;
5699             },
5700
5701             "nodeValue" : function(c, v){
5702                 var r = [], ri = -1;
5703                 for(var i = 0, ci; ci = c[i]; i++){
5704                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5705                         r[++ri] = ci;
5706                     }
5707                 }
5708                 return r;
5709             },
5710
5711             "checked" : function(c){
5712                 var r = [], ri = -1;
5713                 for(var i = 0, ci; ci = c[i]; i++){
5714                     if(ci.checked == true){
5715                         r[++ri] = ci;
5716                     }
5717                 }
5718                 return r;
5719             },
5720
5721             "not" : function(c, ss){
5722                 return Roo.DomQuery.filter(c, ss, true);
5723             },
5724
5725             "odd" : function(c){
5726                 return this["nth-child"](c, "odd");
5727             },
5728
5729             "even" : function(c){
5730                 return this["nth-child"](c, "even");
5731             },
5732
5733             "nth" : function(c, a){
5734                 return c[a-1] || [];
5735             },
5736
5737             "first" : function(c){
5738                 return c[0] || [];
5739             },
5740
5741             "last" : function(c){
5742                 return c[c.length-1] || [];
5743             },
5744
5745             "has" : function(c, ss){
5746                 var s = Roo.DomQuery.select;
5747                 var r = [], ri = -1;
5748                 for(var i = 0, ci; ci = c[i]; i++){
5749                     if(s(ss, ci).length > 0){
5750                         r[++ri] = ci;
5751                     }
5752                 }
5753                 return r;
5754             },
5755
5756             "next" : function(c, ss){
5757                 var is = Roo.DomQuery.is;
5758                 var r = [], ri = -1;
5759                 for(var i = 0, ci; ci = c[i]; i++){
5760                     var n = next(ci);
5761                     if(n && is(n, ss)){
5762                         r[++ri] = ci;
5763                     }
5764                 }
5765                 return r;
5766             },
5767
5768             "prev" : function(c, ss){
5769                 var is = Roo.DomQuery.is;
5770                 var r = [], ri = -1;
5771                 for(var i = 0, ci; ci = c[i]; i++){
5772                     var n = prev(ci);
5773                     if(n && is(n, ss)){
5774                         r[++ri] = ci;
5775                     }
5776                 }
5777                 return r;
5778             }
5779         }
5780     };
5781 }();
5782
5783 /**
5784  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5785  * @param {String} path The selector/xpath query
5786  * @param {Node} root (optional) The start of the query (defaults to document).
5787  * @return {Array}
5788  * @member Roo
5789  * @method query
5790  */
5791 Roo.query = Roo.DomQuery.select;
5792 /*
5793  * Based on:
5794  * Ext JS Library 1.1.1
5795  * Copyright(c) 2006-2007, Ext JS, LLC.
5796  *
5797  * Originally Released Under LGPL - original licence link has changed is not relivant.
5798  *
5799  * Fork - LGPL
5800  * <script type="text/javascript">
5801  */
5802
5803 /**
5804  * @class Roo.util.Observable
5805  * Base class that provides a common interface for publishing events. Subclasses are expected to
5806  * to have a property "events" with all the events defined.<br>
5807  * For example:
5808  * <pre><code>
5809  Employee = function(name){
5810     this.name = name;
5811     this.addEvents({
5812         "fired" : true,
5813         "quit" : true
5814     });
5815  }
5816  Roo.extend(Employee, Roo.util.Observable);
5817 </code></pre>
5818  * @param {Object} config properties to use (incuding events / listeners)
5819  */
5820
5821 Roo.util.Observable = function(cfg){
5822     
5823     cfg = cfg|| {};
5824     this.addEvents(cfg.events || {});
5825     if (cfg.events) {
5826         delete cfg.events; // make sure
5827     }
5828      
5829     Roo.apply(this, cfg);
5830     
5831     if(this.listeners){
5832         this.on(this.listeners);
5833         delete this.listeners;
5834     }
5835 };
5836 Roo.util.Observable.prototype = {
5837     /** 
5838  * @cfg {Object} listeners  list of events and functions to call for this object, 
5839  * For example :
5840  * <pre><code>
5841     listeners :  { 
5842        'click' : function(e) {
5843            ..... 
5844         } ,
5845         .... 
5846     } 
5847   </code></pre>
5848  */
5849     
5850     
5851     /**
5852      * Fires the specified event with the passed parameters (minus the event name).
5853      * @param {String} eventName
5854      * @param {Object...} args Variable number of parameters are passed to handlers
5855      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5856      */
5857     fireEvent : function(){
5858         var ce = this.events[arguments[0].toLowerCase()];
5859         if(typeof ce == "object"){
5860             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5861         }else{
5862             return true;
5863         }
5864     },
5865
5866     // private
5867     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5868
5869     /**
5870      * Appends an event handler to this component
5871      * @param {String}   eventName The type of event to listen for
5872      * @param {Function} handler The method the event invokes
5873      * @param {Object}   scope (optional) The scope in which to execute the handler
5874      * function. The handler function's "this" context.
5875      * @param {Object}   options (optional) An object containing handler configuration
5876      * properties. This may contain any of the following properties:<ul>
5877      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5878      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5879      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5880      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5881      * by the specified number of milliseconds. If the event fires again within that time, the original
5882      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5883      * </ul><br>
5884      * <p>
5885      * <b>Combining Options</b><br>
5886      * Using the options argument, it is possible to combine different types of listeners:<br>
5887      * <br>
5888      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5889                 <pre><code>
5890                 el.on('click', this.onClick, this, {
5891                         single: true,
5892                 delay: 100,
5893                 forumId: 4
5894                 });
5895                 </code></pre>
5896      * <p>
5897      * <b>Attaching multiple handlers in 1 call</b><br>
5898      * The method also allows for a single argument to be passed which is a config object containing properties
5899      * which specify multiple handlers.
5900      * <pre><code>
5901                 el.on({
5902                         'click': {
5903                         fn: this.onClick,
5904                         scope: this,
5905                         delay: 100
5906                 }, 
5907                 'mouseover': {
5908                         fn: this.onMouseOver,
5909                         scope: this
5910                 },
5911                 'mouseout': {
5912                         fn: this.onMouseOut,
5913                         scope: this
5914                 }
5915                 });
5916                 </code></pre>
5917      * <p>
5918      * Or a shorthand syntax which passes the same scope object to all handlers:
5919         <pre><code>
5920                 el.on({
5921                         'click': this.onClick,
5922                 'mouseover': this.onMouseOver,
5923                 'mouseout': this.onMouseOut,
5924                 scope: this
5925                 });
5926                 </code></pre>
5927      */
5928     addListener : function(eventName, fn, scope, o){
5929         if(typeof eventName == "object"){
5930             o = eventName;
5931             for(var e in o){
5932                 if(this.filterOptRe.test(e)){
5933                     continue;
5934                 }
5935                 if(typeof o[e] == "function"){
5936                     // shared options
5937                     this.addListener(e, o[e], o.scope,  o);
5938                 }else{
5939                     // individual options
5940                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5941                 }
5942             }
5943             return;
5944         }
5945         o = (!o || typeof o == "boolean") ? {} : o;
5946         eventName = eventName.toLowerCase();
5947         var ce = this.events[eventName] || true;
5948         if(typeof ce == "boolean"){
5949             ce = new Roo.util.Event(this, eventName);
5950             this.events[eventName] = ce;
5951         }
5952         ce.addListener(fn, scope, o);
5953     },
5954
5955     /**
5956      * Removes a listener
5957      * @param {String}   eventName     The type of event to listen for
5958      * @param {Function} handler        The handler to remove
5959      * @param {Object}   scope  (optional) The scope (this object) for the handler
5960      */
5961     removeListener : function(eventName, fn, scope){
5962         var ce = this.events[eventName.toLowerCase()];
5963         if(typeof ce == "object"){
5964             ce.removeListener(fn, scope);
5965         }
5966     },
5967
5968     /**
5969      * Removes all listeners for this object
5970      */
5971     purgeListeners : function(){
5972         for(var evt in this.events){
5973             if(typeof this.events[evt] == "object"){
5974                  this.events[evt].clearListeners();
5975             }
5976         }
5977     },
5978
5979     relayEvents : function(o, events){
5980         var createHandler = function(ename){
5981             return function(){
5982                  
5983                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5984             };
5985         };
5986         for(var i = 0, len = events.length; i < len; i++){
5987             var ename = events[i];
5988             if(!this.events[ename]){
5989                 this.events[ename] = true;
5990             };
5991             o.on(ename, createHandler(ename), this);
5992         }
5993     },
5994
5995     /**
5996      * Used to define events on this Observable
5997      * @param {Object} object The object with the events defined
5998      */
5999     addEvents : function(o){
6000         if(!this.events){
6001             this.events = {};
6002         }
6003         Roo.applyIf(this.events, o);
6004     },
6005
6006     /**
6007      * Checks to see if this object has any listeners for a specified event
6008      * @param {String} eventName The name of the event to check for
6009      * @return {Boolean} True if the event is being listened for, else false
6010      */
6011     hasListener : function(eventName){
6012         var e = this.events[eventName];
6013         return typeof e == "object" && e.listeners.length > 0;
6014     }
6015 };
6016 /**
6017  * Appends an event handler to this element (shorthand for addListener)
6018  * @param {String}   eventName     The type of event to listen for
6019  * @param {Function} handler        The method the event invokes
6020  * @param {Object}   scope (optional) The scope in which to execute the handler
6021  * function. The handler function's "this" context.
6022  * @param {Object}   options  (optional)
6023  * @method
6024  */
6025 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6026 /**
6027  * Removes a listener (shorthand for removeListener)
6028  * @param {String}   eventName     The type of event to listen for
6029  * @param {Function} handler        The handler to remove
6030  * @param {Object}   scope  (optional) The scope (this object) for the handler
6031  * @method
6032  */
6033 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6034
6035 /**
6036  * Starts capture on the specified Observable. All events will be passed
6037  * to the supplied function with the event name + standard signature of the event
6038  * <b>before</b> the event is fired. If the supplied function returns false,
6039  * the event will not fire.
6040  * @param {Observable} o The Observable to capture
6041  * @param {Function} fn The function to call
6042  * @param {Object} scope (optional) The scope (this object) for the fn
6043  * @static
6044  */
6045 Roo.util.Observable.capture = function(o, fn, scope){
6046     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6047 };
6048
6049 /**
6050  * Removes <b>all</b> added captures from the Observable.
6051  * @param {Observable} o The Observable to release
6052  * @static
6053  */
6054 Roo.util.Observable.releaseCapture = function(o){
6055     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6056 };
6057
6058 (function(){
6059
6060     var createBuffered = function(h, o, scope){
6061         var task = new Roo.util.DelayedTask();
6062         return function(){
6063             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6064         };
6065     };
6066
6067     var createSingle = function(h, e, fn, scope){
6068         return function(){
6069             e.removeListener(fn, scope);
6070             return h.apply(scope, arguments);
6071         };
6072     };
6073
6074     var createDelayed = function(h, o, scope){
6075         return function(){
6076             var args = Array.prototype.slice.call(arguments, 0);
6077             setTimeout(function(){
6078                 h.apply(scope, args);
6079             }, o.delay || 10);
6080         };
6081     };
6082
6083     Roo.util.Event = function(obj, name){
6084         this.name = name;
6085         this.obj = obj;
6086         this.listeners = [];
6087     };
6088
6089     Roo.util.Event.prototype = {
6090         addListener : function(fn, scope, options){
6091             var o = options || {};
6092             scope = scope || this.obj;
6093             if(!this.isListening(fn, scope)){
6094                 var l = {fn: fn, scope: scope, options: o};
6095                 var h = fn;
6096                 if(o.delay){
6097                     h = createDelayed(h, o, scope);
6098                 }
6099                 if(o.single){
6100                     h = createSingle(h, this, fn, scope);
6101                 }
6102                 if(o.buffer){
6103                     h = createBuffered(h, o, scope);
6104                 }
6105                 l.fireFn = h;
6106                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6107                     this.listeners.push(l);
6108                 }else{
6109                     this.listeners = this.listeners.slice(0);
6110                     this.listeners.push(l);
6111                 }
6112             }
6113         },
6114
6115         findListener : function(fn, scope){
6116             scope = scope || this.obj;
6117             var ls = this.listeners;
6118             for(var i = 0, len = ls.length; i < len; i++){
6119                 var l = ls[i];
6120                 if(l.fn == fn && l.scope == scope){
6121                     return i;
6122                 }
6123             }
6124             return -1;
6125         },
6126
6127         isListening : function(fn, scope){
6128             return this.findListener(fn, scope) != -1;
6129         },
6130
6131         removeListener : function(fn, scope){
6132             var index;
6133             if((index = this.findListener(fn, scope)) != -1){
6134                 if(!this.firing){
6135                     this.listeners.splice(index, 1);
6136                 }else{
6137                     this.listeners = this.listeners.slice(0);
6138                     this.listeners.splice(index, 1);
6139                 }
6140                 return true;
6141             }
6142             return false;
6143         },
6144
6145         clearListeners : function(){
6146             this.listeners = [];
6147         },
6148
6149         fire : function(){
6150             var ls = this.listeners, scope, len = ls.length;
6151             if(len > 0){
6152                 this.firing = true;
6153                 var args = Array.prototype.slice.call(arguments, 0);                
6154                 for(var i = 0; i < len; i++){
6155                     var l = ls[i];
6156                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6157                         this.firing = false;
6158                         return false;
6159                     }
6160                 }
6161                 this.firing = false;
6162             }
6163             return true;
6164         }
6165     };
6166 })();/*
6167  * RooJS Library 
6168  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6169  *
6170  * Licence LGPL 
6171  *
6172  */
6173  
6174 /**
6175  * @class Roo.Document
6176  * @extends Roo.util.Observable
6177  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6178  * 
6179  * @param {Object} config the methods and properties of the 'base' class for the application.
6180  * 
6181  *  Generic Page handler - implement this to start your app..
6182  * 
6183  * eg.
6184  *  MyProject = new Roo.Document({
6185         events : {
6186             'load' : true // your events..
6187         },
6188         listeners : {
6189             'ready' : function() {
6190                 // fired on Roo.onReady()
6191             }
6192         }
6193  * 
6194  */
6195 Roo.Document = function(cfg) {
6196      
6197     this.addEvents({ 
6198         'ready' : true
6199     });
6200     Roo.util.Observable.call(this,cfg);
6201     
6202     var _this = this;
6203     
6204     Roo.onReady(function() {
6205         _this.fireEvent('ready');
6206     },null,false);
6207     
6208     
6209 }
6210
6211 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6212  * Based on:
6213  * Ext JS Library 1.1.1
6214  * Copyright(c) 2006-2007, Ext JS, LLC.
6215  *
6216  * Originally Released Under LGPL - original licence link has changed is not relivant.
6217  *
6218  * Fork - LGPL
6219  * <script type="text/javascript">
6220  */
6221
6222 /**
6223  * @class Roo.EventManager
6224  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6225  * several useful events directly.
6226  * See {@link Roo.EventObject} for more details on normalized event objects.
6227  * @singleton
6228  */
6229 Roo.EventManager = function(){
6230     var docReadyEvent, docReadyProcId, docReadyState = false;
6231     var resizeEvent, resizeTask, textEvent, textSize;
6232     var E = Roo.lib.Event;
6233     var D = Roo.lib.Dom;
6234
6235     
6236     
6237
6238     var fireDocReady = function(){
6239         if(!docReadyState){
6240             docReadyState = true;
6241             Roo.isReady = true;
6242             if(docReadyProcId){
6243                 clearInterval(docReadyProcId);
6244             }
6245             if(Roo.isGecko || Roo.isOpera) {
6246                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6247             }
6248             if(Roo.isIE){
6249                 var defer = document.getElementById("ie-deferred-loader");
6250                 if(defer){
6251                     defer.onreadystatechange = null;
6252                     defer.parentNode.removeChild(defer);
6253                 }
6254             }
6255             if(docReadyEvent){
6256                 docReadyEvent.fire();
6257                 docReadyEvent.clearListeners();
6258             }
6259         }
6260     };
6261     
6262     var initDocReady = function(){
6263         docReadyEvent = new Roo.util.Event();
6264         if(Roo.isGecko || Roo.isOpera) {
6265             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6266         }else if(Roo.isIE){
6267             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6268             var defer = document.getElementById("ie-deferred-loader");
6269             defer.onreadystatechange = function(){
6270                 if(this.readyState == "complete"){
6271                     fireDocReady();
6272                 }
6273             };
6274         }else if(Roo.isSafari){ 
6275             docReadyProcId = setInterval(function(){
6276                 var rs = document.readyState;
6277                 if(rs == "complete") {
6278                     fireDocReady();     
6279                  }
6280             }, 10);
6281         }
6282         // no matter what, make sure it fires on load
6283         E.on(window, "load", fireDocReady);
6284     };
6285
6286     var createBuffered = function(h, o){
6287         var task = new Roo.util.DelayedTask(h);
6288         return function(e){
6289             // create new event object impl so new events don't wipe out properties
6290             e = new Roo.EventObjectImpl(e);
6291             task.delay(o.buffer, h, null, [e]);
6292         };
6293     };
6294
6295     var createSingle = function(h, el, ename, fn){
6296         return function(e){
6297             Roo.EventManager.removeListener(el, ename, fn);
6298             h(e);
6299         };
6300     };
6301
6302     var createDelayed = function(h, o){
6303         return function(e){
6304             // create new event object impl so new events don't wipe out properties
6305             e = new Roo.EventObjectImpl(e);
6306             setTimeout(function(){
6307                 h(e);
6308             }, o.delay || 10);
6309         };
6310     };
6311     var transitionEndVal = false;
6312     
6313     var transitionEnd = function()
6314     {
6315         if (transitionEndVal) {
6316             return transitionEndVal;
6317         }
6318         var el = document.createElement('div');
6319
6320         var transEndEventNames = {
6321             WebkitTransition : 'webkitTransitionEnd',
6322             MozTransition    : 'transitionend',
6323             OTransition      : 'oTransitionEnd otransitionend',
6324             transition       : 'transitionend'
6325         };
6326     
6327         for (var name in transEndEventNames) {
6328             if (el.style[name] !== undefined) {
6329                 transitionEndVal = transEndEventNames[name];
6330                 return  transitionEndVal ;
6331             }
6332         }
6333     }
6334     
6335   
6336
6337     var listen = function(element, ename, opt, fn, scope)
6338     {
6339         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6340         fn = fn || o.fn; scope = scope || o.scope;
6341         var el = Roo.getDom(element);
6342         
6343         
6344         if(!el){
6345             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6346         }
6347         
6348         if (ename == 'transitionend') {
6349             ename = transitionEnd();
6350         }
6351         var h = function(e){
6352             e = Roo.EventObject.setEvent(e);
6353             var t;
6354             if(o.delegate){
6355                 t = e.getTarget(o.delegate, el);
6356                 if(!t){
6357                     return;
6358                 }
6359             }else{
6360                 t = e.target;
6361             }
6362             if(o.stopEvent === true){
6363                 e.stopEvent();
6364             }
6365             if(o.preventDefault === true){
6366                e.preventDefault();
6367             }
6368             if(o.stopPropagation === true){
6369                 e.stopPropagation();
6370             }
6371
6372             if(o.normalized === false){
6373                 e = e.browserEvent;
6374             }
6375
6376             fn.call(scope || el, e, t, o);
6377         };
6378         if(o.delay){
6379             h = createDelayed(h, o);
6380         }
6381         if(o.single){
6382             h = createSingle(h, el, ename, fn);
6383         }
6384         if(o.buffer){
6385             h = createBuffered(h, o);
6386         }
6387         
6388         fn._handlers = fn._handlers || [];
6389         
6390         
6391         fn._handlers.push([Roo.id(el), ename, h]);
6392         
6393         
6394          
6395         E.on(el, ename, h); // this adds the actuall listener to the object..
6396         
6397         
6398         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6399             el.addEventListener("DOMMouseScroll", h, false);
6400             E.on(window, 'unload', function(){
6401                 el.removeEventListener("DOMMouseScroll", h, false);
6402             });
6403         }
6404         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6405             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6406         }
6407         return h;
6408     };
6409
6410     var stopListening = function(el, ename, fn){
6411         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6412         if(hds){
6413             for(var i = 0, len = hds.length; i < len; i++){
6414                 var h = hds[i];
6415                 if(h[0] == id && h[1] == ename){
6416                     hd = h[2];
6417                     hds.splice(i, 1);
6418                     break;
6419                 }
6420             }
6421         }
6422         E.un(el, ename, hd);
6423         el = Roo.getDom(el);
6424         if(ename == "mousewheel" && el.addEventListener){
6425             el.removeEventListener("DOMMouseScroll", hd, false);
6426         }
6427         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6428             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6429         }
6430     };
6431
6432     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6433     
6434     var pub = {
6435         
6436         
6437         /** 
6438          * Fix for doc tools
6439          * @scope Roo.EventManager
6440          */
6441         
6442         
6443         /** 
6444          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6445          * object with a Roo.EventObject
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An object that becomes the scope of the handler
6448          * @param {boolean}  override If true, the obj passed in becomes
6449          *                             the execution scope of the listener
6450          * @return {Function} The wrapped function
6451          * @deprecated
6452          */
6453         wrap : function(fn, scope, override){
6454             return function(e){
6455                 Roo.EventObject.setEvent(e);
6456                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6457             };
6458         },
6459         
6460         /**
6461      * Appends an event handler to an element (shorthand for addListener)
6462      * @param {String/HTMLElement}   element        The html element or id to assign the
6463      * @param {String}   eventName The type of event to listen for
6464      * @param {Function} handler The method the event invokes
6465      * @param {Object}   scope (optional) The scope in which to execute the handler
6466      * function. The handler function's "this" context.
6467      * @param {Object}   options (optional) An object containing handler configuration
6468      * properties. This may contain any of the following properties:<ul>
6469      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6470      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6471      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6472      * <li>preventDefault {Boolean} True to prevent the default action</li>
6473      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6474      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6475      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6476      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6477      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6478      * by the specified number of milliseconds. If the event fires again within that time, the original
6479      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6480      * </ul><br>
6481      * <p>
6482      * <b>Combining Options</b><br>
6483      * Using the options argument, it is possible to combine different types of listeners:<br>
6484      * <br>
6485      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6486      * Code:<pre><code>
6487 el.on('click', this.onClick, this, {
6488     single: true,
6489     delay: 100,
6490     stopEvent : true,
6491     forumId: 4
6492 });</code></pre>
6493      * <p>
6494      * <b>Attaching multiple handlers in 1 call</b><br>
6495       * The method also allows for a single argument to be passed which is a config object containing properties
6496      * which specify multiple handlers.
6497      * <p>
6498      * Code:<pre><code>
6499 el.on({
6500     'click' : {
6501         fn: this.onClick
6502         scope: this,
6503         delay: 100
6504     },
6505     'mouseover' : {
6506         fn: this.onMouseOver
6507         scope: this
6508     },
6509     'mouseout' : {
6510         fn: this.onMouseOut
6511         scope: this
6512     }
6513 });</code></pre>
6514      * <p>
6515      * Or a shorthand syntax:<br>
6516      * Code:<pre><code>
6517 el.on({
6518     'click' : this.onClick,
6519     'mouseover' : this.onMouseOver,
6520     'mouseout' : this.onMouseOut
6521     scope: this
6522 });</code></pre>
6523      */
6524         addListener : function(element, eventName, fn, scope, options){
6525             if(typeof eventName == "object"){
6526                 var o = eventName;
6527                 for(var e in o){
6528                     if(propRe.test(e)){
6529                         continue;
6530                     }
6531                     if(typeof o[e] == "function"){
6532                         // shared options
6533                         listen(element, e, o, o[e], o.scope);
6534                     }else{
6535                         // individual options
6536                         listen(element, e, o[e]);
6537                     }
6538                 }
6539                 return;
6540             }
6541             return listen(element, eventName, options, fn, scope);
6542         },
6543         
6544         /**
6545          * Removes an event handler
6546          *
6547          * @param {String/HTMLElement}   element        The id or html element to remove the 
6548          *                             event from
6549          * @param {String}   eventName     The type of event
6550          * @param {Function} fn
6551          * @return {Boolean} True if a listener was actually removed
6552          */
6553         removeListener : function(element, eventName, fn){
6554             return stopListening(element, eventName, fn);
6555         },
6556         
6557         /**
6558          * Fires when the document is ready (before onload and before images are loaded). Can be 
6559          * accessed shorthanded Roo.onReady().
6560          * @param {Function} fn        The method the event invokes
6561          * @param {Object}   scope    An  object that becomes the scope of the handler
6562          * @param {boolean}  options
6563          */
6564         onDocumentReady : function(fn, scope, options){
6565             if(docReadyState){ // if it already fired
6566                 docReadyEvent.addListener(fn, scope, options);
6567                 docReadyEvent.fire();
6568                 docReadyEvent.clearListeners();
6569                 return;
6570             }
6571             if(!docReadyEvent){
6572                 initDocReady();
6573             }
6574             docReadyEvent.addListener(fn, scope, options);
6575         },
6576         
6577         /**
6578          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6579          * @param {Function} fn        The method the event invokes
6580          * @param {Object}   scope    An object that becomes the scope of the handler
6581          * @param {boolean}  options
6582          */
6583         onWindowResize : function(fn, scope, options){
6584             if(!resizeEvent){
6585                 resizeEvent = new Roo.util.Event();
6586                 resizeTask = new Roo.util.DelayedTask(function(){
6587                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6588                 });
6589                 E.on(window, "resize", function(){
6590                     if(Roo.isIE){
6591                         resizeTask.delay(50);
6592                     }else{
6593                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6594                     }
6595                 });
6596             }
6597             resizeEvent.addListener(fn, scope, options);
6598         },
6599
6600         /**
6601          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6602          * @param {Function} fn        The method the event invokes
6603          * @param {Object}   scope    An object that becomes the scope of the handler
6604          * @param {boolean}  options
6605          */
6606         onTextResize : function(fn, scope, options){
6607             if(!textEvent){
6608                 textEvent = new Roo.util.Event();
6609                 var textEl = new Roo.Element(document.createElement('div'));
6610                 textEl.dom.className = 'x-text-resize';
6611                 textEl.dom.innerHTML = 'X';
6612                 textEl.appendTo(document.body);
6613                 textSize = textEl.dom.offsetHeight;
6614                 setInterval(function(){
6615                     if(textEl.dom.offsetHeight != textSize){
6616                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6617                     }
6618                 }, this.textResizeInterval);
6619             }
6620             textEvent.addListener(fn, scope, options);
6621         },
6622
6623         /**
6624          * Removes the passed window resize listener.
6625          * @param {Function} fn        The method the event invokes
6626          * @param {Object}   scope    The scope of handler
6627          */
6628         removeResizeListener : function(fn, scope){
6629             if(resizeEvent){
6630                 resizeEvent.removeListener(fn, scope);
6631             }
6632         },
6633
6634         // private
6635         fireResize : function(){
6636             if(resizeEvent){
6637                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6638             }   
6639         },
6640         /**
6641          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6642          */
6643         ieDeferSrc : false,
6644         /**
6645          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6646          */
6647         textResizeInterval : 50
6648     };
6649     
6650     /**
6651      * Fix for doc tools
6652      * @scopeAlias pub=Roo.EventManager
6653      */
6654     
6655      /**
6656      * Appends an event handler to an element (shorthand for addListener)
6657      * @param {String/HTMLElement}   element        The html element or id to assign the
6658      * @param {String}   eventName The type of event to listen for
6659      * @param {Function} handler The method the event invokes
6660      * @param {Object}   scope (optional) The scope in which to execute the handler
6661      * function. The handler function's "this" context.
6662      * @param {Object}   options (optional) An object containing handler configuration
6663      * properties. This may contain any of the following properties:<ul>
6664      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6665      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6666      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6667      * <li>preventDefault {Boolean} True to prevent the default action</li>
6668      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6669      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6670      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6671      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6672      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6673      * by the specified number of milliseconds. If the event fires again within that time, the original
6674      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6675      * </ul><br>
6676      * <p>
6677      * <b>Combining Options</b><br>
6678      * Using the options argument, it is possible to combine different types of listeners:<br>
6679      * <br>
6680      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6681      * Code:<pre><code>
6682 el.on('click', this.onClick, this, {
6683     single: true,
6684     delay: 100,
6685     stopEvent : true,
6686     forumId: 4
6687 });</code></pre>
6688      * <p>
6689      * <b>Attaching multiple handlers in 1 call</b><br>
6690       * The method also allows for a single argument to be passed which is a config object containing properties
6691      * which specify multiple handlers.
6692      * <p>
6693      * Code:<pre><code>
6694 el.on({
6695     'click' : {
6696         fn: this.onClick
6697         scope: this,
6698         delay: 100
6699     },
6700     'mouseover' : {
6701         fn: this.onMouseOver
6702         scope: this
6703     },
6704     'mouseout' : {
6705         fn: this.onMouseOut
6706         scope: this
6707     }
6708 });</code></pre>
6709      * <p>
6710      * Or a shorthand syntax:<br>
6711      * Code:<pre><code>
6712 el.on({
6713     'click' : this.onClick,
6714     'mouseover' : this.onMouseOver,
6715     'mouseout' : this.onMouseOut
6716     scope: this
6717 });</code></pre>
6718      */
6719     pub.on = pub.addListener;
6720     pub.un = pub.removeListener;
6721
6722     pub.stoppedMouseDownEvent = new Roo.util.Event();
6723     return pub;
6724 }();
6725 /**
6726   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6727   * @param {Function} fn        The method the event invokes
6728   * @param {Object}   scope    An  object that becomes the scope of the handler
6729   * @param {boolean}  override If true, the obj passed in becomes
6730   *                             the execution scope of the listener
6731   * @member Roo
6732   * @method onReady
6733  */
6734 Roo.onReady = Roo.EventManager.onDocumentReady;
6735
6736 Roo.onReady(function(){
6737     var bd = Roo.get(document.body);
6738     if(!bd){ return; }
6739
6740     var cls = [
6741             Roo.isIE ? "roo-ie"
6742             : Roo.isIE11 ? "roo-ie11"
6743             : Roo.isEdge ? "roo-edge"
6744             : Roo.isGecko ? "roo-gecko"
6745             : Roo.isOpera ? "roo-opera"
6746             : Roo.isSafari ? "roo-safari" : ""];
6747
6748     if(Roo.isMac){
6749         cls.push("roo-mac");
6750     }
6751     if(Roo.isLinux){
6752         cls.push("roo-linux");
6753     }
6754     if(Roo.isIOS){
6755         cls.push("roo-ios");
6756     }
6757     if(Roo.isTouch){
6758         cls.push("roo-touch");
6759     }
6760     if(Roo.isBorderBox){
6761         cls.push('roo-border-box');
6762     }
6763     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6764         var p = bd.dom.parentNode;
6765         if(p){
6766             p.className += ' roo-strict';
6767         }
6768     }
6769     bd.addClass(cls.join(' '));
6770 });
6771
6772 /**
6773  * @class Roo.EventObject
6774  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6775  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6776  * Example:
6777  * <pre><code>
6778  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6779     e.preventDefault();
6780     var target = e.getTarget();
6781     ...
6782  }
6783  var myDiv = Roo.get("myDiv");
6784  myDiv.on("click", handleClick);
6785  //or
6786  Roo.EventManager.on("myDiv", 'click', handleClick);
6787  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6788  </code></pre>
6789  * @singleton
6790  */
6791 Roo.EventObject = function(){
6792     
6793     var E = Roo.lib.Event;
6794     
6795     // safari keypress events for special keys return bad keycodes
6796     var safariKeys = {
6797         63234 : 37, // left
6798         63235 : 39, // right
6799         63232 : 38, // up
6800         63233 : 40, // down
6801         63276 : 33, // page up
6802         63277 : 34, // page down
6803         63272 : 46, // delete
6804         63273 : 36, // home
6805         63275 : 35  // end
6806     };
6807
6808     // normalize button clicks
6809     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6810                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6811
6812     Roo.EventObjectImpl = function(e){
6813         if(e){
6814             this.setEvent(e.browserEvent || e);
6815         }
6816     };
6817     Roo.EventObjectImpl.prototype = {
6818         /**
6819          * Used to fix doc tools.
6820          * @scope Roo.EventObject.prototype
6821          */
6822             
6823
6824         
6825         
6826         /** The normal browser event */
6827         browserEvent : null,
6828         /** The button pressed in a mouse event */
6829         button : -1,
6830         /** True if the shift key was down during the event */
6831         shiftKey : false,
6832         /** True if the control key was down during the event */
6833         ctrlKey : false,
6834         /** True if the alt key was down during the event */
6835         altKey : false,
6836
6837         /** Key constant 
6838         * @type Number */
6839         BACKSPACE : 8,
6840         /** Key constant 
6841         * @type Number */
6842         TAB : 9,
6843         /** Key constant 
6844         * @type Number */
6845         RETURN : 13,
6846         /** Key constant 
6847         * @type Number */
6848         ENTER : 13,
6849         /** Key constant 
6850         * @type Number */
6851         SHIFT : 16,
6852         /** Key constant 
6853         * @type Number */
6854         CONTROL : 17,
6855         /** Key constant 
6856         * @type Number */
6857         ESC : 27,
6858         /** Key constant 
6859         * @type Number */
6860         SPACE : 32,
6861         /** Key constant 
6862         * @type Number */
6863         PAGEUP : 33,
6864         /** Key constant 
6865         * @type Number */
6866         PAGEDOWN : 34,
6867         /** Key constant 
6868         * @type Number */
6869         END : 35,
6870         /** Key constant 
6871         * @type Number */
6872         HOME : 36,
6873         /** Key constant 
6874         * @type Number */
6875         LEFT : 37,
6876         /** Key constant 
6877         * @type Number */
6878         UP : 38,
6879         /** Key constant 
6880         * @type Number */
6881         RIGHT : 39,
6882         /** Key constant 
6883         * @type Number */
6884         DOWN : 40,
6885         /** Key constant 
6886         * @type Number */
6887         DELETE : 46,
6888         /** Key constant 
6889         * @type Number */
6890         F5 : 116,
6891
6892            /** @private */
6893         setEvent : function(e){
6894             if(e == this || (e && e.browserEvent)){ // already wrapped
6895                 return e;
6896             }
6897             this.browserEvent = e;
6898             if(e){
6899                 // normalize buttons
6900                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6901                 if(e.type == 'click' && this.button == -1){
6902                     this.button = 0;
6903                 }
6904                 this.type = e.type;
6905                 this.shiftKey = e.shiftKey;
6906                 // mac metaKey behaves like ctrlKey
6907                 this.ctrlKey = e.ctrlKey || e.metaKey;
6908                 this.altKey = e.altKey;
6909                 // in getKey these will be normalized for the mac
6910                 this.keyCode = e.keyCode;
6911                 // keyup warnings on firefox.
6912                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6913                 // cache the target for the delayed and or buffered events
6914                 this.target = E.getTarget(e);
6915                 // same for XY
6916                 this.xy = E.getXY(e);
6917             }else{
6918                 this.button = -1;
6919                 this.shiftKey = false;
6920                 this.ctrlKey = false;
6921                 this.altKey = false;
6922                 this.keyCode = 0;
6923                 this.charCode =0;
6924                 this.target = null;
6925                 this.xy = [0, 0];
6926             }
6927             return this;
6928         },
6929
6930         /**
6931          * Stop the event (preventDefault and stopPropagation)
6932          */
6933         stopEvent : function(){
6934             if(this.browserEvent){
6935                 if(this.browserEvent.type == 'mousedown'){
6936                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6937                 }
6938                 E.stopEvent(this.browserEvent);
6939             }
6940         },
6941
6942         /**
6943          * Prevents the browsers default handling of the event.
6944          */
6945         preventDefault : function(){
6946             if(this.browserEvent){
6947                 E.preventDefault(this.browserEvent);
6948             }
6949         },
6950
6951         /** @private */
6952         isNavKeyPress : function(){
6953             var k = this.keyCode;
6954             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6955             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6956         },
6957
6958         isSpecialKey : function(){
6959             var k = this.keyCode;
6960             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6961             (k == 16) || (k == 17) ||
6962             (k >= 18 && k <= 20) ||
6963             (k >= 33 && k <= 35) ||
6964             (k >= 36 && k <= 39) ||
6965             (k >= 44 && k <= 45);
6966         },
6967         /**
6968          * Cancels bubbling of the event.
6969          */
6970         stopPropagation : function(){
6971             if(this.browserEvent){
6972                 if(this.type == 'mousedown'){
6973                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6974                 }
6975                 E.stopPropagation(this.browserEvent);
6976             }
6977         },
6978
6979         /**
6980          * Gets the key code for the event.
6981          * @return {Number}
6982          */
6983         getCharCode : function(){
6984             return this.charCode || this.keyCode;
6985         },
6986
6987         /**
6988          * Returns a normalized keyCode for the event.
6989          * @return {Number} The key code
6990          */
6991         getKey : function(){
6992             var k = this.keyCode || this.charCode;
6993             return Roo.isSafari ? (safariKeys[k] || k) : k;
6994         },
6995
6996         /**
6997          * Gets the x coordinate of the event.
6998          * @return {Number}
6999          */
7000         getPageX : function(){
7001             return this.xy[0];
7002         },
7003
7004         /**
7005          * Gets the y coordinate of the event.
7006          * @return {Number}
7007          */
7008         getPageY : function(){
7009             return this.xy[1];
7010         },
7011
7012         /**
7013          * Gets the time of the event.
7014          * @return {Number}
7015          */
7016         getTime : function(){
7017             if(this.browserEvent){
7018                 return E.getTime(this.browserEvent);
7019             }
7020             return null;
7021         },
7022
7023         /**
7024          * Gets the page coordinates of the event.
7025          * @return {Array} The xy values like [x, y]
7026          */
7027         getXY : function(){
7028             return this.xy;
7029         },
7030
7031         /**
7032          * Gets the target for the event.
7033          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLelement}
7038          */
7039         getTarget : function(selector, maxDepth, returnEl){
7040             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7041         },
7042         /**
7043          * Gets the related target.
7044          * @return {HTMLElement}
7045          */
7046         getRelatedTarget : function(){
7047             if(this.browserEvent){
7048                 return E.getRelatedTarget(this.browserEvent);
7049             }
7050             return null;
7051         },
7052
7053         /**
7054          * Normalizes mouse wheel delta across browsers
7055          * @return {Number} The delta
7056          */
7057         getWheelDelta : function(){
7058             var e = this.browserEvent;
7059             var delta = 0;
7060             if(e.wheelDelta){ /* IE/Opera. */
7061                 delta = e.wheelDelta/120;
7062             }else if(e.detail){ /* Mozilla case. */
7063                 delta = -e.detail/3;
7064             }
7065             return delta;
7066         },
7067
7068         /**
7069          * Returns true if the control, meta, shift or alt key was pressed during this event.
7070          * @return {Boolean}
7071          */
7072         hasModifier : function(){
7073             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7074         },
7075
7076         /**
7077          * Returns true if the target of this event equals el or is a child of el
7078          * @param {String/HTMLElement/Element} el
7079          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7080          * @return {Boolean}
7081          */
7082         within : function(el, related){
7083             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7084             return t && Roo.fly(el).contains(t);
7085         },
7086
7087         getPoint : function(){
7088             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7089         }
7090     };
7091
7092     return new Roo.EventObjectImpl();
7093 }();
7094             
7095     /*
7096  * Based on:
7097  * Ext JS Library 1.1.1
7098  * Copyright(c) 2006-2007, Ext JS, LLC.
7099  *
7100  * Originally Released Under LGPL - original licence link has changed is not relivant.
7101  *
7102  * Fork - LGPL
7103  * <script type="text/javascript">
7104  */
7105
7106  
7107 // was in Composite Element!??!?!
7108  
7109 (function(){
7110     var D = Roo.lib.Dom;
7111     var E = Roo.lib.Event;
7112     var A = Roo.lib.Anim;
7113
7114     // local style camelizing for speed
7115     var propCache = {};
7116     var camelRe = /(-[a-z])/gi;
7117     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7118     var view = document.defaultView;
7119
7120 /**
7121  * @class Roo.Element
7122  * Represents an Element in the DOM.<br><br>
7123  * Usage:<br>
7124 <pre><code>
7125 var el = Roo.get("my-div");
7126
7127 // or with getEl
7128 var el = getEl("my-div");
7129
7130 // or with a DOM element
7131 var el = Roo.get(myDivElement);
7132 </code></pre>
7133  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7134  * each call instead of constructing a new one.<br><br>
7135  * <b>Animations</b><br />
7136  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7137  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7138 <pre>
7139 Option    Default   Description
7140 --------- --------  ---------------------------------------------
7141 duration  .35       The duration of the animation in seconds
7142 easing    easeOut   The YUI easing method
7143 callback  none      A function to execute when the anim completes
7144 scope     this      The scope (this) of the callback function
7145 </pre>
7146 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7147 * manipulate the animation. Here's an example:
7148 <pre><code>
7149 var el = Roo.get("my-div");
7150
7151 // no animation
7152 el.setWidth(100);
7153
7154 // default animation
7155 el.setWidth(100, true);
7156
7157 // animation with some options set
7158 el.setWidth(100, {
7159     duration: 1,
7160     callback: this.foo,
7161     scope: this
7162 });
7163
7164 // using the "anim" property to get the Anim object
7165 var opt = {
7166     duration: 1,
7167     callback: this.foo,
7168     scope: this
7169 };
7170 el.setWidth(100, opt);
7171 ...
7172 if(opt.anim.isAnimated()){
7173     opt.anim.stop();
7174 }
7175 </code></pre>
7176 * <b> Composite (Collections of) Elements</b><br />
7177  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7178  * @constructor Create a new Element directly.
7179  * @param {String/HTMLElement} element
7180  * @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).
7181  */
7182     Roo.Element = function(element, forceNew)
7183     {
7184         var dom = typeof element == "string" ?
7185                 document.getElementById(element) : element;
7186         
7187         this.listeners = {};
7188         
7189         if(!dom){ // invalid id/element
7190             return null;
7191         }
7192         var id = dom.id;
7193         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7194             return Roo.Element.cache[id];
7195         }
7196
7197         /**
7198          * The DOM element
7199          * @type HTMLElement
7200          */
7201         this.dom = dom;
7202
7203         /**
7204          * The DOM element ID
7205          * @type String
7206          */
7207         this.id = id || Roo.id(dom);
7208         
7209         return this; // assumed for cctor?
7210     };
7211
7212     var El = Roo.Element;
7213
7214     El.prototype = {
7215         /**
7216          * The element's default display mode  (defaults to "") 
7217          * @type String
7218          */
7219         originalDisplay : "",
7220
7221         
7222         // note this is overridden in BS version..
7223         visibilityMode : 1, 
7224         /**
7225          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7226          * @type String
7227          */
7228         defaultUnit : "px",
7229         
7230         /**
7231          * Sets the element's visibility mode. When setVisible() is called it
7232          * will use this to determine whether to set the visibility or the display property.
7233          * @param visMode Element.VISIBILITY or Element.DISPLAY
7234          * @return {Roo.Element} this
7235          */
7236         setVisibilityMode : function(visMode){
7237             this.visibilityMode = visMode;
7238             return this;
7239         },
7240         /**
7241          * Convenience method for setVisibilityMode(Element.DISPLAY)
7242          * @param {String} display (optional) What to set display to when visible
7243          * @return {Roo.Element} this
7244          */
7245         enableDisplayMode : function(display){
7246             this.setVisibilityMode(El.DISPLAY);
7247             if(typeof display != "undefined") { this.originalDisplay = display; }
7248             return this;
7249         },
7250
7251         /**
7252          * 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)
7253          * @param {String} selector The simple selector to test
7254          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7255                 search as a number or element (defaults to 10 || document.body)
7256          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7257          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7258          */
7259         findParent : function(simpleSelector, maxDepth, returnEl){
7260             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7261             maxDepth = maxDepth || 50;
7262             if(typeof maxDepth != "number"){
7263                 stopEl = Roo.getDom(maxDepth);
7264                 maxDepth = 10;
7265             }
7266             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7267                 if(dq.is(p, simpleSelector)){
7268                     return returnEl ? Roo.get(p) : p;
7269                 }
7270                 depth++;
7271                 p = p.parentNode;
7272             }
7273             return null;
7274         },
7275
7276
7277         /**
7278          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7279          * @param {String} selector The simple selector to test
7280          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7281                 search as a number or element (defaults to 10 || document.body)
7282          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7283          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7284          */
7285         findParentNode : function(simpleSelector, maxDepth, returnEl){
7286             var p = Roo.fly(this.dom.parentNode, '_internal');
7287             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7288         },
7289         
7290         /**
7291          * Looks at  the scrollable parent element
7292          */
7293         findScrollableParent : function()
7294         {
7295             var overflowRegex = /(auto|scroll)/;
7296             
7297             if(this.getStyle('position') === 'fixed'){
7298                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7299             }
7300             
7301             var excludeStaticParent = this.getStyle('position') === "absolute";
7302             
7303             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7304                 
7305                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7306                     continue;
7307                 }
7308                 
7309                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7310                     return parent;
7311                 }
7312                 
7313                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7314                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7315                 }
7316             }
7317             
7318             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7319         },
7320
7321         /**
7322          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7323          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7324          * @param {String} selector The simple selector to test
7325          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7326                 search as a number or element (defaults to 10 || document.body)
7327          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7328          */
7329         up : function(simpleSelector, maxDepth){
7330             return this.findParentNode(simpleSelector, maxDepth, true);
7331         },
7332
7333
7334
7335         /**
7336          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7337          * @param {String} selector The simple selector to test
7338          * @return {Boolean} True if this element matches the selector, else false
7339          */
7340         is : function(simpleSelector){
7341             return Roo.DomQuery.is(this.dom, simpleSelector);
7342         },
7343
7344         /**
7345          * Perform animation on this element.
7346          * @param {Object} args The YUI animation control args
7347          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7348          * @param {Function} onComplete (optional) Function to call when animation completes
7349          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7350          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7351          * @return {Roo.Element} this
7352          */
7353         animate : function(args, duration, onComplete, easing, animType){
7354             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7355             return this;
7356         },
7357
7358         /*
7359          * @private Internal animation call
7360          */
7361         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7362             animType = animType || 'run';
7363             opt = opt || {};
7364             var anim = Roo.lib.Anim[animType](
7365                 this.dom, args,
7366                 (opt.duration || defaultDur) || .35,
7367                 (opt.easing || defaultEase) || 'easeOut',
7368                 function(){
7369                     Roo.callback(cb, this);
7370                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7371                 },
7372                 this
7373             );
7374             opt.anim = anim;
7375             return anim;
7376         },
7377
7378         // private legacy anim prep
7379         preanim : function(a, i){
7380             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7381         },
7382
7383         /**
7384          * Removes worthless text nodes
7385          * @param {Boolean} forceReclean (optional) By default the element
7386          * keeps track if it has been cleaned already so
7387          * you can call this over and over. However, if you update the element and
7388          * need to force a reclean, you can pass true.
7389          */
7390         clean : function(forceReclean){
7391             if(this.isCleaned && forceReclean !== true){
7392                 return this;
7393             }
7394             var ns = /\S/;
7395             var d = this.dom, n = d.firstChild, ni = -1;
7396             while(n){
7397                 var nx = n.nextSibling;
7398                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7399                     d.removeChild(n);
7400                 }else{
7401                     n.nodeIndex = ++ni;
7402                 }
7403                 n = nx;
7404             }
7405             this.isCleaned = true;
7406             return this;
7407         },
7408
7409         // private
7410         calcOffsetsTo : function(el){
7411             el = Roo.get(el);
7412             var d = el.dom;
7413             var restorePos = false;
7414             if(el.getStyle('position') == 'static'){
7415                 el.position('relative');
7416                 restorePos = true;
7417             }
7418             var x = 0, y =0;
7419             var op = this.dom;
7420             while(op && op != d && op.tagName != 'HTML'){
7421                 x+= op.offsetLeft;
7422                 y+= op.offsetTop;
7423                 op = op.offsetParent;
7424             }
7425             if(restorePos){
7426                 el.position('static');
7427             }
7428             return [x, y];
7429         },
7430
7431         /**
7432          * Scrolls this element into view within the passed container.
7433          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7434          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7435          * @return {Roo.Element} this
7436          */
7437         scrollIntoView : function(container, hscroll){
7438             var c = Roo.getDom(container) || document.body;
7439             var el = this.dom;
7440
7441             var o = this.calcOffsetsTo(c),
7442                 l = o[0],
7443                 t = o[1],
7444                 b = t+el.offsetHeight,
7445                 r = l+el.offsetWidth;
7446
7447             var ch = c.clientHeight;
7448             var ct = parseInt(c.scrollTop, 10);
7449             var cl = parseInt(c.scrollLeft, 10);
7450             var cb = ct + ch;
7451             var cr = cl + c.clientWidth;
7452
7453             if(t < ct){
7454                 c.scrollTop = t;
7455             }else if(b > cb){
7456                 c.scrollTop = b-ch;
7457             }
7458
7459             if(hscroll !== false){
7460                 if(l < cl){
7461                     c.scrollLeft = l;
7462                 }else if(r > cr){
7463                     c.scrollLeft = r-c.clientWidth;
7464                 }
7465             }
7466             return this;
7467         },
7468
7469         // private
7470         scrollChildIntoView : function(child, hscroll){
7471             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7472         },
7473
7474         /**
7475          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7476          * the new height may not be available immediately.
7477          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7478          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7479          * @param {Function} onComplete (optional) Function to call when animation completes
7480          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7481          * @return {Roo.Element} this
7482          */
7483         autoHeight : function(animate, duration, onComplete, easing){
7484             var oldHeight = this.getHeight();
7485             this.clip();
7486             this.setHeight(1); // force clipping
7487             setTimeout(function(){
7488                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7489                 if(!animate){
7490                     this.setHeight(height);
7491                     this.unclip();
7492                     if(typeof onComplete == "function"){
7493                         onComplete();
7494                     }
7495                 }else{
7496                     this.setHeight(oldHeight); // restore original height
7497                     this.setHeight(height, animate, duration, function(){
7498                         this.unclip();
7499                         if(typeof onComplete == "function") { onComplete(); }
7500                     }.createDelegate(this), easing);
7501                 }
7502             }.createDelegate(this), 0);
7503             return this;
7504         },
7505
7506         /**
7507          * Returns true if this element is an ancestor of the passed element
7508          * @param {HTMLElement/String} el The element to check
7509          * @return {Boolean} True if this element is an ancestor of el, else false
7510          */
7511         contains : function(el){
7512             if(!el){return false;}
7513             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7514         },
7515
7516         /**
7517          * Checks whether the element is currently visible using both visibility and display properties.
7518          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7519          * @return {Boolean} True if the element is currently visible, else false
7520          */
7521         isVisible : function(deep) {
7522             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7523             if(deep !== true || !vis){
7524                 return vis;
7525             }
7526             var p = this.dom.parentNode;
7527             while(p && p.tagName.toLowerCase() != "body"){
7528                 if(!Roo.fly(p, '_isVisible').isVisible()){
7529                     return false;
7530                 }
7531                 p = p.parentNode;
7532             }
7533             return true;
7534         },
7535
7536         /**
7537          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7538          * @param {String} selector The CSS selector
7539          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7540          * @return {CompositeElement/CompositeElementLite} The composite element
7541          */
7542         select : function(selector, unique){
7543             return El.select(selector, unique, this.dom);
7544         },
7545
7546         /**
7547          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7548          * @param {String} selector The CSS selector
7549          * @return {Array} An array of the matched nodes
7550          */
7551         query : function(selector, unique){
7552             return Roo.DomQuery.select(selector, this.dom);
7553         },
7554
7555         /**
7556          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7557          * @param {String} selector The CSS selector
7558          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7559          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7560          */
7561         child : function(selector, returnDom){
7562             var n = Roo.DomQuery.selectNode(selector, this.dom);
7563             return returnDom ? n : Roo.get(n);
7564         },
7565
7566         /**
7567          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7568          * @param {String} selector The CSS selector
7569          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7570          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7571          */
7572         down : function(selector, returnDom){
7573             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7574             return returnDom ? n : Roo.get(n);
7575         },
7576
7577         /**
7578          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7579          * @param {String} group The group the DD object is member of
7580          * @param {Object} config The DD config object
7581          * @param {Object} overrides An object containing methods to override/implement on the DD object
7582          * @return {Roo.dd.DD} The DD object
7583          */
7584         initDD : function(group, config, overrides){
7585             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7586             return Roo.apply(dd, overrides);
7587         },
7588
7589         /**
7590          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7591          * @param {String} group The group the DDProxy object is member of
7592          * @param {Object} config The DDProxy config object
7593          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7594          * @return {Roo.dd.DDProxy} The DDProxy object
7595          */
7596         initDDProxy : function(group, config, overrides){
7597             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7598             return Roo.apply(dd, overrides);
7599         },
7600
7601         /**
7602          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7603          * @param {String} group The group the DDTarget object is member of
7604          * @param {Object} config The DDTarget config object
7605          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7606          * @return {Roo.dd.DDTarget} The DDTarget object
7607          */
7608         initDDTarget : function(group, config, overrides){
7609             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7610             return Roo.apply(dd, overrides);
7611         },
7612
7613         /**
7614          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7615          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7616          * @param {Boolean} visible Whether the element is visible
7617          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7618          * @return {Roo.Element} this
7619          */
7620          setVisible : function(visible, animate){
7621             if(!animate || !A){
7622                 if(this.visibilityMode == El.DISPLAY){
7623                     this.setDisplayed(visible);
7624                 }else{
7625                     this.fixDisplay();
7626                     this.dom.style.visibility = visible ? "visible" : "hidden";
7627                 }
7628             }else{
7629                 // closure for composites
7630                 var dom = this.dom;
7631                 var visMode = this.visibilityMode;
7632                 if(visible){
7633                     this.setOpacity(.01);
7634                     this.setVisible(true);
7635                 }
7636                 this.anim({opacity: { to: (visible?1:0) }},
7637                       this.preanim(arguments, 1),
7638                       null, .35, 'easeIn', function(){
7639                          if(!visible){
7640                              if(visMode == El.DISPLAY){
7641                                  dom.style.display = "none";
7642                              }else{
7643                                  dom.style.visibility = "hidden";
7644                              }
7645                              Roo.get(dom).setOpacity(1);
7646                          }
7647                      });
7648             }
7649             return this;
7650         },
7651
7652         /**
7653          * Returns true if display is not "none"
7654          * @return {Boolean}
7655          */
7656         isDisplayed : function() {
7657             return this.getStyle("display") != "none";
7658         },
7659
7660         /**
7661          * Toggles the element's visibility or display, depending on visibility mode.
7662          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7663          * @return {Roo.Element} this
7664          */
7665         toggle : function(animate){
7666             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7667             return this;
7668         },
7669
7670         /**
7671          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7672          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7673          * @return {Roo.Element} this
7674          */
7675         setDisplayed : function(value) {
7676             if(typeof value == "boolean"){
7677                value = value ? this.originalDisplay : "none";
7678             }
7679             this.setStyle("display", value);
7680             return this;
7681         },
7682
7683         /**
7684          * Tries to focus the element. Any exceptions are caught and ignored.
7685          * @return {Roo.Element} this
7686          */
7687         focus : function() {
7688             try{
7689                 this.dom.focus();
7690             }catch(e){}
7691             return this;
7692         },
7693
7694         /**
7695          * Tries to blur the element. Any exceptions are caught and ignored.
7696          * @return {Roo.Element} this
7697          */
7698         blur : function() {
7699             try{
7700                 this.dom.blur();
7701             }catch(e){}
7702             return this;
7703         },
7704
7705         /**
7706          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7707          * @param {String/Array} className The CSS class to add, or an array of classes
7708          * @return {Roo.Element} this
7709          */
7710         addClass : function(className){
7711             if(className instanceof Array){
7712                 for(var i = 0, len = className.length; i < len; i++) {
7713                     this.addClass(className[i]);
7714                 }
7715             }else{
7716                 if(className && !this.hasClass(className)){
7717                     if (this.dom instanceof SVGElement) {
7718                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7719                     } else {
7720                         this.dom.className = this.dom.className + " " + className;
7721                     }
7722                 }
7723             }
7724             return this;
7725         },
7726
7727         /**
7728          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7729          * @param {String/Array} className The CSS class to add, or an array of classes
7730          * @return {Roo.Element} this
7731          */
7732         radioClass : function(className){
7733             var siblings = this.dom.parentNode.childNodes;
7734             for(var i = 0; i < siblings.length; i++) {
7735                 var s = siblings[i];
7736                 if(s.nodeType == 1){
7737                     Roo.get(s).removeClass(className);
7738                 }
7739             }
7740             this.addClass(className);
7741             return this;
7742         },
7743
7744         /**
7745          * Removes one or more CSS classes from the element.
7746          * @param {String/Array} className The CSS class to remove, or an array of classes
7747          * @return {Roo.Element} this
7748          */
7749         removeClass : function(className){
7750             
7751             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7752             if(!className || !cn){
7753                 return this;
7754             }
7755             if(className instanceof Array){
7756                 for(var i = 0, len = className.length; i < len; i++) {
7757                     this.removeClass(className[i]);
7758                 }
7759             }else{
7760                 if(this.hasClass(className)){
7761                     var re = this.classReCache[className];
7762                     if (!re) {
7763                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7764                        this.classReCache[className] = re;
7765                     }
7766                     if (this.dom instanceof SVGElement) {
7767                         this.dom.className.baseVal = cn.replace(re, " ");
7768                     } else {
7769                         this.dom.className = cn.replace(re, " ");
7770                     }
7771                 }
7772             }
7773             return this;
7774         },
7775
7776         // private
7777         classReCache: {},
7778
7779         /**
7780          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7781          * @param {String} className The CSS class to toggle
7782          * @return {Roo.Element} this
7783          */
7784         toggleClass : function(className){
7785             if(this.hasClass(className)){
7786                 this.removeClass(className);
7787             }else{
7788                 this.addClass(className);
7789             }
7790             return this;
7791         },
7792
7793         /**
7794          * Checks if the specified CSS class exists on this element's DOM node.
7795          * @param {String} className The CSS class to check for
7796          * @return {Boolean} True if the class exists, else false
7797          */
7798         hasClass : function(className){
7799             if (this.dom instanceof SVGElement) {
7800                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7801             } 
7802             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7803         },
7804
7805         /**
7806          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7807          * @param {String} oldClassName The CSS class to replace
7808          * @param {String} newClassName The replacement CSS class
7809          * @return {Roo.Element} this
7810          */
7811         replaceClass : function(oldClassName, newClassName){
7812             this.removeClass(oldClassName);
7813             this.addClass(newClassName);
7814             return this;
7815         },
7816
7817         /**
7818          * Returns an object with properties matching the styles requested.
7819          * For example, el.getStyles('color', 'font-size', 'width') might return
7820          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7821          * @param {String} style1 A style name
7822          * @param {String} style2 A style name
7823          * @param {String} etc.
7824          * @return {Object} The style object
7825          */
7826         getStyles : function(){
7827             var a = arguments, len = a.length, r = {};
7828             for(var i = 0; i < len; i++){
7829                 r[a[i]] = this.getStyle(a[i]);
7830             }
7831             return r;
7832         },
7833
7834         /**
7835          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7836          * @param {String} property The style property whose value is returned.
7837          * @return {String} The current value of the style property for this element.
7838          */
7839         getStyle : function(){
7840             return view && view.getComputedStyle ?
7841                 function(prop){
7842                     var el = this.dom, v, cs, camel;
7843                     if(prop == 'float'){
7844                         prop = "cssFloat";
7845                     }
7846                     if(el.style && (v = el.style[prop])){
7847                         return v;
7848                     }
7849                     if(cs = view.getComputedStyle(el, "")){
7850                         if(!(camel = propCache[prop])){
7851                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7852                         }
7853                         return cs[camel];
7854                     }
7855                     return null;
7856                 } :
7857                 function(prop){
7858                     var el = this.dom, v, cs, camel;
7859                     if(prop == 'opacity'){
7860                         if(typeof el.style.filter == 'string'){
7861                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7862                             if(m){
7863                                 var fv = parseFloat(m[1]);
7864                                 if(!isNaN(fv)){
7865                                     return fv ? fv / 100 : 0;
7866                                 }
7867                             }
7868                         }
7869                         return 1;
7870                     }else if(prop == 'float'){
7871                         prop = "styleFloat";
7872                     }
7873                     if(!(camel = propCache[prop])){
7874                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7875                     }
7876                     if(v = el.style[camel]){
7877                         return v;
7878                     }
7879                     if(cs = el.currentStyle){
7880                         return cs[camel];
7881                     }
7882                     return null;
7883                 };
7884         }(),
7885
7886         /**
7887          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7888          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7889          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7890          * @return {Roo.Element} this
7891          */
7892         setStyle : function(prop, value){
7893             if(typeof prop == "string"){
7894                 
7895                 if (prop == 'float') {
7896                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7897                     return this;
7898                 }
7899                 
7900                 var camel;
7901                 if(!(camel = propCache[prop])){
7902                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7903                 }
7904                 
7905                 if(camel == 'opacity') {
7906                     this.setOpacity(value);
7907                 }else{
7908                     this.dom.style[camel] = value;
7909                 }
7910             }else{
7911                 for(var style in prop){
7912                     if(typeof prop[style] != "function"){
7913                        this.setStyle(style, prop[style]);
7914                     }
7915                 }
7916             }
7917             return this;
7918         },
7919
7920         /**
7921          * More flexible version of {@link #setStyle} for setting style properties.
7922          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7923          * a function which returns such a specification.
7924          * @return {Roo.Element} this
7925          */
7926         applyStyles : function(style){
7927             Roo.DomHelper.applyStyles(this.dom, style);
7928             return this;
7929         },
7930
7931         /**
7932           * 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).
7933           * @return {Number} The X position of the element
7934           */
7935         getX : function(){
7936             return D.getX(this.dom);
7937         },
7938
7939         /**
7940           * 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).
7941           * @return {Number} The Y position of the element
7942           */
7943         getY : function(){
7944             return D.getY(this.dom);
7945         },
7946
7947         /**
7948           * 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).
7949           * @return {Array} The XY position of the element
7950           */
7951         getXY : function(){
7952             return D.getXY(this.dom);
7953         },
7954
7955         /**
7956          * 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).
7957          * @param {Number} The X position of the element
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         setX : function(x, animate){
7962             if(!animate || !A){
7963                 D.setX(this.dom, x);
7964             }else{
7965                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7966             }
7967             return this;
7968         },
7969
7970         /**
7971          * 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).
7972          * @param {Number} The Y position of the element
7973          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7974          * @return {Roo.Element} this
7975          */
7976         setY : function(y, animate){
7977             if(!animate || !A){
7978                 D.setY(this.dom, y);
7979             }else{
7980                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7981             }
7982             return this;
7983         },
7984
7985         /**
7986          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7987          * @param {String} left The left CSS property value
7988          * @return {Roo.Element} this
7989          */
7990         setLeft : function(left){
7991             this.setStyle("left", this.addUnits(left));
7992             return this;
7993         },
7994
7995         /**
7996          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7997          * @param {String} top The top CSS property value
7998          * @return {Roo.Element} this
7999          */
8000         setTop : function(top){
8001             this.setStyle("top", this.addUnits(top));
8002             return this;
8003         },
8004
8005         /**
8006          * Sets the element's CSS right style.
8007          * @param {String} right The right CSS property value
8008          * @return {Roo.Element} this
8009          */
8010         setRight : function(right){
8011             this.setStyle("right", this.addUnits(right));
8012             return this;
8013         },
8014
8015         /**
8016          * Sets the element's CSS bottom style.
8017          * @param {String} bottom The bottom CSS property value
8018          * @return {Roo.Element} this
8019          */
8020         setBottom : function(bottom){
8021             this.setStyle("bottom", this.addUnits(bottom));
8022             return this;
8023         },
8024
8025         /**
8026          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8027          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8028          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8029          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032         setXY : function(pos, animate){
8033             if(!animate || !A){
8034                 D.setXY(this.dom, pos);
8035             }else{
8036                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8043          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8044          * @param {Number} x X value for new position (coordinates are page-based)
8045          * @param {Number} y Y value for new position (coordinates are page-based)
8046          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8047          * @return {Roo.Element} this
8048          */
8049         setLocation : function(x, y, animate){
8050             this.setXY([x, y], this.preanim(arguments, 2));
8051             return this;
8052         },
8053
8054         /**
8055          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8056          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8057          * @param {Number} x X value for new position (coordinates are page-based)
8058          * @param {Number} y Y value for new position (coordinates are page-based)
8059          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8060          * @return {Roo.Element} this
8061          */
8062         moveTo : function(x, y, animate){
8063             this.setXY([x, y], this.preanim(arguments, 2));
8064             return this;
8065         },
8066
8067         /**
8068          * Returns the region of the given element.
8069          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8070          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8071          */
8072         getRegion : function(){
8073             return D.getRegion(this.dom);
8074         },
8075
8076         /**
8077          * Returns the offset height of the element
8078          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8079          * @return {Number} The element's height
8080          */
8081         getHeight : function(contentHeight){
8082             var h = this.dom.offsetHeight || 0;
8083             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8084         },
8085
8086         /**
8087          * Returns the offset width of the element
8088          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8089          * @return {Number} The element's width
8090          */
8091         getWidth : function(contentWidth){
8092             var w = this.dom.offsetWidth || 0;
8093             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8094         },
8095
8096         /**
8097          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8098          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8099          * if a height has not been set using CSS.
8100          * @return {Number}
8101          */
8102         getComputedHeight : function(){
8103             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8104             if(!h){
8105                 h = parseInt(this.getStyle('height'), 10) || 0;
8106                 if(!this.isBorderBox()){
8107                     h += this.getFrameWidth('tb');
8108                 }
8109             }
8110             return h;
8111         },
8112
8113         /**
8114          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8115          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8116          * if a width has not been set using CSS.
8117          * @return {Number}
8118          */
8119         getComputedWidth : function(){
8120             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8121             if(!w){
8122                 w = parseInt(this.getStyle('width'), 10) || 0;
8123                 if(!this.isBorderBox()){
8124                     w += this.getFrameWidth('lr');
8125                 }
8126             }
8127             return w;
8128         },
8129
8130         /**
8131          * Returns the size of the element.
8132          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8133          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8134          */
8135         getSize : function(contentSize){
8136             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8137         },
8138
8139         /**
8140          * Returns the width and height of the viewport.
8141          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8142          */
8143         getViewSize : function(){
8144             var d = this.dom, doc = document, aw = 0, ah = 0;
8145             if(d == doc || d == doc.body){
8146                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8147             }else{
8148                 return {
8149                     width : d.clientWidth,
8150                     height: d.clientHeight
8151                 };
8152             }
8153         },
8154
8155         /**
8156          * Returns the value of the "value" attribute
8157          * @param {Boolean} asNumber true to parse the value as a number
8158          * @return {String/Number}
8159          */
8160         getValue : function(asNumber){
8161             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8162         },
8163
8164         // private
8165         adjustWidth : function(width){
8166             if(typeof width == "number"){
8167                 if(this.autoBoxAdjust && !this.isBorderBox()){
8168                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8169                 }
8170                 if(width < 0){
8171                     width = 0;
8172                 }
8173             }
8174             return width;
8175         },
8176
8177         // private
8178         adjustHeight : function(height){
8179             if(typeof height == "number"){
8180                if(this.autoBoxAdjust && !this.isBorderBox()){
8181                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8182                }
8183                if(height < 0){
8184                    height = 0;
8185                }
8186             }
8187             return height;
8188         },
8189
8190         /**
8191          * Set the width of the element
8192          * @param {Number} width The new width
8193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8194          * @return {Roo.Element} this
8195          */
8196         setWidth : function(width, animate){
8197             width = this.adjustWidth(width);
8198             if(!animate || !A){
8199                 this.dom.style.width = this.addUnits(width);
8200             }else{
8201                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          * Set the height of the element
8208          * @param {Number} height The new height
8209          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8210          * @return {Roo.Element} this
8211          */
8212          setHeight : function(height, animate){
8213             height = this.adjustHeight(height);
8214             if(!animate || !A){
8215                 this.dom.style.height = this.addUnits(height);
8216             }else{
8217                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8218             }
8219             return this;
8220         },
8221
8222         /**
8223          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8224          * @param {Number} width The new width
8225          * @param {Number} height The new height
8226          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8227          * @return {Roo.Element} this
8228          */
8229          setSize : function(width, height, animate){
8230             if(typeof width == "object"){ // in case of object from getSize()
8231                 height = width.height; width = width.width;
8232             }
8233             width = this.adjustWidth(width); height = this.adjustHeight(height);
8234             if(!animate || !A){
8235                 this.dom.style.width = this.addUnits(width);
8236                 this.dom.style.height = this.addUnits(height);
8237             }else{
8238                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8239             }
8240             return this;
8241         },
8242
8243         /**
8244          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8245          * @param {Number} x X value for new position (coordinates are page-based)
8246          * @param {Number} y Y value for new position (coordinates are page-based)
8247          * @param {Number} width The new width
8248          * @param {Number} height The new height
8249          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8250          * @return {Roo.Element} this
8251          */
8252         setBounds : function(x, y, width, height, animate){
8253             if(!animate || !A){
8254                 this.setSize(width, height);
8255                 this.setLocation(x, y);
8256             }else{
8257                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8258                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8259                               this.preanim(arguments, 4), 'motion');
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          * 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.
8266          * @param {Roo.lib.Region} region The region to fill
8267          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8268          * @return {Roo.Element} this
8269          */
8270         setRegion : function(region, animate){
8271             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8272             return this;
8273         },
8274
8275         /**
8276          * Appends an event handler
8277          *
8278          * @param {String}   eventName     The type of event to append
8279          * @param {Function} fn        The method the event invokes
8280          * @param {Object} scope       (optional) The scope (this object) of the fn
8281          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8282          */
8283         addListener : function(eventName, fn, scope, options)
8284         {
8285             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8286                 this.addListener('touchstart', this.onTapHandler, this);
8287             }
8288             
8289             // we need to handle a special case where dom element is a svg element.
8290             // in this case we do not actua
8291             if (!this.dom) {
8292                 return;
8293             }
8294             
8295             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8296                 if (typeof(this.listeners[eventName]) == 'undefined') {
8297                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8298                 }
8299                 this.listeners[eventName].addListener(fn, scope, options);
8300                 return;
8301             }
8302             
8303                 
8304             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8305             
8306             
8307         },
8308         tapedTwice : false,
8309         onTapHandler : function(event)
8310         {
8311             if(!this.tapedTwice) {
8312                 this.tapedTwice = true;
8313                 var s = this;
8314                 setTimeout( function() {
8315                     s.tapedTwice = false;
8316                 }, 300 );
8317                 return;
8318             }
8319             event.preventDefault();
8320             var revent = new MouseEvent('dblclick',  {
8321                 view: window,
8322                 bubbles: true,
8323                 cancelable: true
8324             });
8325              
8326             this.dom.dispatchEvent(revent);
8327             //action on double tap goes below
8328              
8329         }, 
8330  
8331         /**
8332          * Removes an event handler from this element
8333          * @param {String} eventName the type of event to remove
8334          * @param {Function} fn the method the event invokes
8335          * @param {Function} scope (needed for svg fake listeners)
8336          * @return {Roo.Element} this
8337          */
8338         removeListener : function(eventName, fn, scope){
8339             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8340             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8341                 return this;
8342             }
8343             this.listeners[eventName].removeListener(fn, scope);
8344             return this;
8345         },
8346
8347         /**
8348          * Removes all previous added listeners from this element
8349          * @return {Roo.Element} this
8350          */
8351         removeAllListeners : function(){
8352             E.purgeElement(this.dom);
8353             this.listeners = {};
8354             return this;
8355         },
8356
8357         relayEvent : function(eventName, observable){
8358             this.on(eventName, function(e){
8359                 observable.fireEvent(eventName, e);
8360             });
8361         },
8362
8363         
8364         /**
8365          * Set the opacity of the element
8366          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8367          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8368          * @return {Roo.Element} this
8369          */
8370          setOpacity : function(opacity, animate){
8371             if(!animate || !A){
8372                 var s = this.dom.style;
8373                 if(Roo.isIE){
8374                     s.zoom = 1;
8375                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8376                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8377                 }else{
8378                     s.opacity = opacity;
8379                 }
8380             }else{
8381                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8382             }
8383             return this;
8384         },
8385
8386         /**
8387          * Gets the left X coordinate
8388          * @param {Boolean} local True to get the local css position instead of page coordinate
8389          * @return {Number}
8390          */
8391         getLeft : function(local){
8392             if(!local){
8393                 return this.getX();
8394             }else{
8395                 return parseInt(this.getStyle("left"), 10) || 0;
8396             }
8397         },
8398
8399         /**
8400          * Gets the right X coordinate of the element (element X position + element width)
8401          * @param {Boolean} local True to get the local css position instead of page coordinate
8402          * @return {Number}
8403          */
8404         getRight : function(local){
8405             if(!local){
8406                 return this.getX() + this.getWidth();
8407             }else{
8408                 return (this.getLeft(true) + this.getWidth()) || 0;
8409             }
8410         },
8411
8412         /**
8413          * Gets the top Y coordinate
8414          * @param {Boolean} local True to get the local css position instead of page coordinate
8415          * @return {Number}
8416          */
8417         getTop : function(local) {
8418             if(!local){
8419                 return this.getY();
8420             }else{
8421                 return parseInt(this.getStyle("top"), 10) || 0;
8422             }
8423         },
8424
8425         /**
8426          * Gets the bottom Y coordinate of the element (element Y position + element height)
8427          * @param {Boolean} local True to get the local css position instead of page coordinate
8428          * @return {Number}
8429          */
8430         getBottom : function(local){
8431             if(!local){
8432                 return this.getY() + this.getHeight();
8433             }else{
8434                 return (this.getTop(true) + this.getHeight()) || 0;
8435             }
8436         },
8437
8438         /**
8439         * Initializes positioning on this element. If a desired position is not passed, it will make the
8440         * the element positioned relative IF it is not already positioned.
8441         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8442         * @param {Number} zIndex (optional) The zIndex to apply
8443         * @param {Number} x (optional) Set the page X position
8444         * @param {Number} y (optional) Set the page Y position
8445         */
8446         position : function(pos, zIndex, x, y){
8447             if(!pos){
8448                if(this.getStyle('position') == 'static'){
8449                    this.setStyle('position', 'relative');
8450                }
8451             }else{
8452                 this.setStyle("position", pos);
8453             }
8454             if(zIndex){
8455                 this.setStyle("z-index", zIndex);
8456             }
8457             if(x !== undefined && y !== undefined){
8458                 this.setXY([x, y]);
8459             }else if(x !== undefined){
8460                 this.setX(x);
8461             }else if(y !== undefined){
8462                 this.setY(y);
8463             }
8464         },
8465
8466         /**
8467         * Clear positioning back to the default when the document was loaded
8468         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8469         * @return {Roo.Element} this
8470          */
8471         clearPositioning : function(value){
8472             value = value ||'';
8473             this.setStyle({
8474                 "left": value,
8475                 "right": value,
8476                 "top": value,
8477                 "bottom": value,
8478                 "z-index": "",
8479                 "position" : "static"
8480             });
8481             return this;
8482         },
8483
8484         /**
8485         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8486         * snapshot before performing an update and then restoring the element.
8487         * @return {Object}
8488         */
8489         getPositioning : function(){
8490             var l = this.getStyle("left");
8491             var t = this.getStyle("top");
8492             return {
8493                 "position" : this.getStyle("position"),
8494                 "left" : l,
8495                 "right" : l ? "" : this.getStyle("right"),
8496                 "top" : t,
8497                 "bottom" : t ? "" : this.getStyle("bottom"),
8498                 "z-index" : this.getStyle("z-index")
8499             };
8500         },
8501
8502         /**
8503          * Gets the width of the border(s) for the specified side(s)
8504          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8505          * passing lr would get the border (l)eft width + the border (r)ight width.
8506          * @return {Number} The width of the sides passed added together
8507          */
8508         getBorderWidth : function(side){
8509             return this.addStyles(side, El.borders);
8510         },
8511
8512         /**
8513          * Gets the width of the padding(s) for the specified side(s)
8514          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8515          * passing lr would get the padding (l)eft + the padding (r)ight.
8516          * @return {Number} The padding of the sides passed added together
8517          */
8518         getPadding : function(side){
8519             return this.addStyles(side, El.paddings);
8520         },
8521
8522         /**
8523         * Set positioning with an object returned by getPositioning().
8524         * @param {Object} posCfg
8525         * @return {Roo.Element} this
8526          */
8527         setPositioning : function(pc){
8528             this.applyStyles(pc);
8529             if(pc.right == "auto"){
8530                 this.dom.style.right = "";
8531             }
8532             if(pc.bottom == "auto"){
8533                 this.dom.style.bottom = "";
8534             }
8535             return this;
8536         },
8537
8538         // private
8539         fixDisplay : function(){
8540             if(this.getStyle("display") == "none"){
8541                 this.setStyle("visibility", "hidden");
8542                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8543                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8544                     this.setStyle("display", "block");
8545                 }
8546             }
8547         },
8548
8549         /**
8550          * Quick set left and top adding default units
8551          * @param {String} left The left CSS property value
8552          * @param {String} top The top CSS property value
8553          * @return {Roo.Element} this
8554          */
8555          setLeftTop : function(left, top){
8556             this.dom.style.left = this.addUnits(left);
8557             this.dom.style.top = this.addUnits(top);
8558             return this;
8559         },
8560
8561         /**
8562          * Move this element relative to its current position.
8563          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8564          * @param {Number} distance How far to move the element in pixels
8565          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8566          * @return {Roo.Element} this
8567          */
8568          move : function(direction, distance, animate){
8569             var xy = this.getXY();
8570             direction = direction.toLowerCase();
8571             switch(direction){
8572                 case "l":
8573                 case "left":
8574                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8575                     break;
8576                case "r":
8577                case "right":
8578                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8579                     break;
8580                case "t":
8581                case "top":
8582                case "up":
8583                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8584                     break;
8585                case "b":
8586                case "bottom":
8587                case "down":
8588                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8589                     break;
8590             }
8591             return this;
8592         },
8593
8594         /**
8595          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8596          * @return {Roo.Element} this
8597          */
8598         clip : function(){
8599             if(!this.isClipped){
8600                this.isClipped = true;
8601                this.originalClip = {
8602                    "o": this.getStyle("overflow"),
8603                    "x": this.getStyle("overflow-x"),
8604                    "y": this.getStyle("overflow-y")
8605                };
8606                this.setStyle("overflow", "hidden");
8607                this.setStyle("overflow-x", "hidden");
8608                this.setStyle("overflow-y", "hidden");
8609             }
8610             return this;
8611         },
8612
8613         /**
8614          *  Return clipping (overflow) to original clipping before clip() was called
8615          * @return {Roo.Element} this
8616          */
8617         unclip : function(){
8618             if(this.isClipped){
8619                 this.isClipped = false;
8620                 var o = this.originalClip;
8621                 if(o.o){this.setStyle("overflow", o.o);}
8622                 if(o.x){this.setStyle("overflow-x", o.x);}
8623                 if(o.y){this.setStyle("overflow-y", o.y);}
8624             }
8625             return this;
8626         },
8627
8628
8629         /**
8630          * Gets the x,y coordinates specified by the anchor position on the element.
8631          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8632          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8633          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8634          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8635          * @return {Array} [x, y] An array containing the element's x and y coordinates
8636          */
8637         getAnchorXY : function(anchor, local, s){
8638             //Passing a different size is useful for pre-calculating anchors,
8639             //especially for anchored animations that change the el size.
8640
8641             var w, h, vp = false;
8642             if(!s){
8643                 var d = this.dom;
8644                 if(d == document.body || d == document){
8645                     vp = true;
8646                     w = D.getViewWidth(); h = D.getViewHeight();
8647                 }else{
8648                     w = this.getWidth(); h = this.getHeight();
8649                 }
8650             }else{
8651                 w = s.width;  h = s.height;
8652             }
8653             var x = 0, y = 0, r = Math.round;
8654             switch((anchor || "tl").toLowerCase()){
8655                 case "c":
8656                     x = r(w*.5);
8657                     y = r(h*.5);
8658                 break;
8659                 case "t":
8660                     x = r(w*.5);
8661                     y = 0;
8662                 break;
8663                 case "l":
8664                     x = 0;
8665                     y = r(h*.5);
8666                 break;
8667                 case "r":
8668                     x = w;
8669                     y = r(h*.5);
8670                 break;
8671                 case "b":
8672                     x = r(w*.5);
8673                     y = h;
8674                 break;
8675                 case "tl":
8676                     x = 0;
8677                     y = 0;
8678                 break;
8679                 case "bl":
8680                     x = 0;
8681                     y = h;
8682                 break;
8683                 case "br":
8684                     x = w;
8685                     y = h;
8686                 break;
8687                 case "tr":
8688                     x = w;
8689                     y = 0;
8690                 break;
8691             }
8692             if(local === true){
8693                 return [x, y];
8694             }
8695             if(vp){
8696                 var sc = this.getScroll();
8697                 return [x + sc.left, y + sc.top];
8698             }
8699             //Add the element's offset xy
8700             var o = this.getXY();
8701             return [x+o[0], y+o[1]];
8702         },
8703
8704         /**
8705          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8706          * supported position values.
8707          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8708          * @param {String} position The position to align to.
8709          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8710          * @return {Array} [x, y]
8711          */
8712         getAlignToXY : function(el, p, o)
8713         {
8714             el = Roo.get(el);
8715             var d = this.dom;
8716             if(!el.dom){
8717                 throw "Element.alignTo with an element that doesn't exist";
8718             }
8719             var c = false; //constrain to viewport
8720             var p1 = "", p2 = "";
8721             o = o || [0,0];
8722
8723             if(!p){
8724                 p = "tl-bl";
8725             }else if(p == "?"){
8726                 p = "tl-bl?";
8727             }else if(p.indexOf("-") == -1){
8728                 p = "tl-" + p;
8729             }
8730             p = p.toLowerCase();
8731             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8732             if(!m){
8733                throw "Element.alignTo with an invalid alignment " + p;
8734             }
8735             p1 = m[1]; p2 = m[2]; c = !!m[3];
8736
8737             //Subtract the aligned el's internal xy from the target's offset xy
8738             //plus custom offset to get the aligned el's new offset xy
8739             var a1 = this.getAnchorXY(p1, true);
8740             var a2 = el.getAnchorXY(p2, false);
8741             var x = a2[0] - a1[0] + o[0];
8742             var y = a2[1] - a1[1] + o[1];
8743             if(c){
8744                 //constrain the aligned el to viewport if necessary
8745                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8746                 // 5px of margin for ie
8747                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8748
8749                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8750                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8751                 //otherwise swap the aligned el to the opposite border of the target.
8752                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8753                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8754                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8755                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8756
8757                var doc = document;
8758                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8759                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8760
8761                if((x+w) > dw + scrollX){
8762                     x = swapX ? r.left-w : dw+scrollX-w;
8763                 }
8764                if(x < scrollX){
8765                    x = swapX ? r.right : scrollX;
8766                }
8767                if((y+h) > dh + scrollY){
8768                     y = swapY ? r.top-h : dh+scrollY-h;
8769                 }
8770                if (y < scrollY){
8771                    y = swapY ? r.bottom : scrollY;
8772                }
8773             }
8774             return [x,y];
8775         },
8776
8777         // private
8778         getConstrainToXY : function(){
8779             var os = {top:0, left:0, bottom:0, right: 0};
8780
8781             return function(el, local, offsets, proposedXY){
8782                 el = Roo.get(el);
8783                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8784
8785                 var vw, vh, vx = 0, vy = 0;
8786                 if(el.dom == document.body || el.dom == document){
8787                     vw = Roo.lib.Dom.getViewWidth();
8788                     vh = Roo.lib.Dom.getViewHeight();
8789                 }else{
8790                     vw = el.dom.clientWidth;
8791                     vh = el.dom.clientHeight;
8792                     if(!local){
8793                         var vxy = el.getXY();
8794                         vx = vxy[0];
8795                         vy = vxy[1];
8796                     }
8797                 }
8798
8799                 var s = el.getScroll();
8800
8801                 vx += offsets.left + s.left;
8802                 vy += offsets.top + s.top;
8803
8804                 vw -= offsets.right;
8805                 vh -= offsets.bottom;
8806
8807                 var vr = vx+vw;
8808                 var vb = vy+vh;
8809
8810                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8811                 var x = xy[0], y = xy[1];
8812                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8813
8814                 // only move it if it needs it
8815                 var moved = false;
8816
8817                 // first validate right/bottom
8818                 if((x + w) > vr){
8819                     x = vr - w;
8820                     moved = true;
8821                 }
8822                 if((y + h) > vb){
8823                     y = vb - h;
8824                     moved = true;
8825                 }
8826                 // then make sure top/left isn't negative
8827                 if(x < vx){
8828                     x = vx;
8829                     moved = true;
8830                 }
8831                 if(y < vy){
8832                     y = vy;
8833                     moved = true;
8834                 }
8835                 return moved ? [x, y] : false;
8836             };
8837         }(),
8838
8839         // private
8840         adjustForConstraints : function(xy, parent, offsets){
8841             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8842         },
8843
8844         /**
8845          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8846          * document it aligns it to the viewport.
8847          * The position parameter is optional, and can be specified in any one of the following formats:
8848          * <ul>
8849          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8850          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8851          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8852          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8853          *   <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
8854          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8855          * </ul>
8856          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8857          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8858          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8859          * that specified in order to enforce the viewport constraints.
8860          * Following are all of the supported anchor positions:
8861     <pre>
8862     Value  Description
8863     -----  -----------------------------
8864     tl     The top left corner (default)
8865     t      The center of the top edge
8866     tr     The top right corner
8867     l      The center of the left edge
8868     c      In the center of the element
8869     r      The center of the right edge
8870     bl     The bottom left corner
8871     b      The center of the bottom edge
8872     br     The bottom right corner
8873     </pre>
8874     Example Usage:
8875     <pre><code>
8876     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8877     el.alignTo("other-el");
8878
8879     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8880     el.alignTo("other-el", "tr?");
8881
8882     // align the bottom right corner of el with the center left edge of other-el
8883     el.alignTo("other-el", "br-l?");
8884
8885     // align the center of el with the bottom left corner of other-el and
8886     // adjust the x position by -6 pixels (and the y position by 0)
8887     el.alignTo("other-el", "c-bl", [-6, 0]);
8888     </code></pre>
8889          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8890          * @param {String} position The position to align to.
8891          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8892          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8893          * @return {Roo.Element} this
8894          */
8895         alignTo : function(element, position, offsets, animate){
8896             var xy = this.getAlignToXY(element, position, offsets);
8897             this.setXY(xy, this.preanim(arguments, 3));
8898             return this;
8899         },
8900
8901         /**
8902          * Anchors an element to another element and realigns it when the window is resized.
8903          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8904          * @param {String} position The position to align to.
8905          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8906          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8907          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8908          * is a number, it is used as the buffer delay (defaults to 50ms).
8909          * @param {Function} callback The function to call after the animation finishes
8910          * @return {Roo.Element} this
8911          */
8912         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8913             var action = function(){
8914                 this.alignTo(el, alignment, offsets, animate);
8915                 Roo.callback(callback, this);
8916             };
8917             Roo.EventManager.onWindowResize(action, this);
8918             var tm = typeof monitorScroll;
8919             if(tm != 'undefined'){
8920                 Roo.EventManager.on(window, 'scroll', action, this,
8921                     {buffer: tm == 'number' ? monitorScroll : 50});
8922             }
8923             action.call(this); // align immediately
8924             return this;
8925         },
8926         /**
8927          * Clears any opacity settings from this element. Required in some cases for IE.
8928          * @return {Roo.Element} this
8929          */
8930         clearOpacity : function(){
8931             if (window.ActiveXObject) {
8932                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8933                     this.dom.style.filter = "";
8934                 }
8935             } else {
8936                 this.dom.style.opacity = "";
8937                 this.dom.style["-moz-opacity"] = "";
8938                 this.dom.style["-khtml-opacity"] = "";
8939             }
8940             return this;
8941         },
8942
8943         /**
8944          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8945          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8946          * @return {Roo.Element} this
8947          */
8948         hide : function(animate){
8949             this.setVisible(false, this.preanim(arguments, 0));
8950             return this;
8951         },
8952
8953         /**
8954         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8955         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8956          * @return {Roo.Element} this
8957          */
8958         show : function(animate){
8959             this.setVisible(true, this.preanim(arguments, 0));
8960             return this;
8961         },
8962
8963         /**
8964          * @private Test if size has a unit, otherwise appends the default
8965          */
8966         addUnits : function(size){
8967             return Roo.Element.addUnits(size, this.defaultUnit);
8968         },
8969
8970         /**
8971          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8972          * @return {Roo.Element} this
8973          */
8974         beginMeasure : function(){
8975             var el = this.dom;
8976             if(el.offsetWidth || el.offsetHeight){
8977                 return this; // offsets work already
8978             }
8979             var changed = [];
8980             var p = this.dom, b = document.body; // start with this element
8981             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8982                 var pe = Roo.get(p);
8983                 if(pe.getStyle('display') == 'none'){
8984                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8985                     p.style.visibility = "hidden";
8986                     p.style.display = "block";
8987                 }
8988                 p = p.parentNode;
8989             }
8990             this._measureChanged = changed;
8991             return this;
8992
8993         },
8994
8995         /**
8996          * Restores displays to before beginMeasure was called
8997          * @return {Roo.Element} this
8998          */
8999         endMeasure : function(){
9000             var changed = this._measureChanged;
9001             if(changed){
9002                 for(var i = 0, len = changed.length; i < len; i++) {
9003                     var r = changed[i];
9004                     r.el.style.visibility = r.visibility;
9005                     r.el.style.display = "none";
9006                 }
9007                 this._measureChanged = null;
9008             }
9009             return this;
9010         },
9011
9012         /**
9013         * Update the innerHTML of this element, optionally searching for and processing scripts
9014         * @param {String} html The new HTML
9015         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9016         * @param {Function} callback For async script loading you can be noticed when the update completes
9017         * @return {Roo.Element} this
9018          */
9019         update : function(html, loadScripts, callback){
9020             if(typeof html == "undefined"){
9021                 html = "";
9022             }
9023             if(loadScripts !== true){
9024                 this.dom.innerHTML = html;
9025                 if(typeof callback == "function"){
9026                     callback();
9027                 }
9028                 return this;
9029             }
9030             var id = Roo.id();
9031             var dom = this.dom;
9032
9033             html += '<span id="' + id + '"></span>';
9034
9035             E.onAvailable(id, function(){
9036                 var hd = document.getElementsByTagName("head")[0];
9037                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9038                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9039                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9040
9041                 var match;
9042                 while(match = re.exec(html)){
9043                     var attrs = match[1];
9044                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9045                     if(srcMatch && srcMatch[2]){
9046                        var s = document.createElement("script");
9047                        s.src = srcMatch[2];
9048                        var typeMatch = attrs.match(typeRe);
9049                        if(typeMatch && typeMatch[2]){
9050                            s.type = typeMatch[2];
9051                        }
9052                        hd.appendChild(s);
9053                     }else if(match[2] && match[2].length > 0){
9054                         if(window.execScript) {
9055                            window.execScript(match[2]);
9056                         } else {
9057                             /**
9058                              * eval:var:id
9059                              * eval:var:dom
9060                              * eval:var:html
9061                              * 
9062                              */
9063                            window.eval(match[2]);
9064                         }
9065                     }
9066                 }
9067                 var el = document.getElementById(id);
9068                 if(el){el.parentNode.removeChild(el);}
9069                 if(typeof callback == "function"){
9070                     callback();
9071                 }
9072             });
9073             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9074             return this;
9075         },
9076
9077         /**
9078          * Direct access to the UpdateManager update() method (takes the same parameters).
9079          * @param {String/Function} url The url for this request or a function to call to get the url
9080          * @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}
9081          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9082          * @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.
9083          * @return {Roo.Element} this
9084          */
9085         load : function(){
9086             var um = this.getUpdateManager();
9087             um.update.apply(um, arguments);
9088             return this;
9089         },
9090
9091         /**
9092         * Gets this element's UpdateManager
9093         * @return {Roo.UpdateManager} The UpdateManager
9094         */
9095         getUpdateManager : function(){
9096             if(!this.updateManager){
9097                 this.updateManager = new Roo.UpdateManager(this);
9098             }
9099             return this.updateManager;
9100         },
9101
9102         /**
9103          * Disables text selection for this element (normalized across browsers)
9104          * @return {Roo.Element} this
9105          */
9106         unselectable : function(){
9107             this.dom.unselectable = "on";
9108             this.swallowEvent("selectstart", true);
9109             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9110             this.addClass("x-unselectable");
9111             return this;
9112         },
9113
9114         /**
9115         * Calculates the x, y to center this element on the screen
9116         * @return {Array} The x, y values [x, y]
9117         */
9118         getCenterXY : function(){
9119             return this.getAlignToXY(document, 'c-c');
9120         },
9121
9122         /**
9123         * Centers the Element in either the viewport, or another Element.
9124         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9125         */
9126         center : function(centerIn){
9127             this.alignTo(centerIn || document, 'c-c');
9128             return this;
9129         },
9130
9131         /**
9132          * Tests various css rules/browsers to determine if this element uses a border box
9133          * @return {Boolean}
9134          */
9135         isBorderBox : function(){
9136             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9137         },
9138
9139         /**
9140          * Return a box {x, y, width, height} that can be used to set another elements
9141          * size/location to match this element.
9142          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9143          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9144          * @return {Object} box An object in the format {x, y, width, height}
9145          */
9146         getBox : function(contentBox, local){
9147             var xy;
9148             if(!local){
9149                 xy = this.getXY();
9150             }else{
9151                 var left = parseInt(this.getStyle("left"), 10) || 0;
9152                 var top = parseInt(this.getStyle("top"), 10) || 0;
9153                 xy = [left, top];
9154             }
9155             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9156             if(!contentBox){
9157                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9158             }else{
9159                 var l = this.getBorderWidth("l")+this.getPadding("l");
9160                 var r = this.getBorderWidth("r")+this.getPadding("r");
9161                 var t = this.getBorderWidth("t")+this.getPadding("t");
9162                 var b = this.getBorderWidth("b")+this.getPadding("b");
9163                 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)};
9164             }
9165             bx.right = bx.x + bx.width;
9166             bx.bottom = bx.y + bx.height;
9167             return bx;
9168         },
9169
9170         /**
9171          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9172          for more information about the sides.
9173          * @param {String} sides
9174          * @return {Number}
9175          */
9176         getFrameWidth : function(sides, onlyContentBox){
9177             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9178         },
9179
9180         /**
9181          * 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.
9182          * @param {Object} box The box to fill {x, y, width, height}
9183          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9184          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9185          * @return {Roo.Element} this
9186          */
9187         setBox : function(box, adjust, animate){
9188             var w = box.width, h = box.height;
9189             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9190                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9191                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9192             }
9193             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9194             return this;
9195         },
9196
9197         /**
9198          * Forces the browser to repaint this element
9199          * @return {Roo.Element} this
9200          */
9201          repaint : function(){
9202             var dom = this.dom;
9203             this.addClass("x-repaint");
9204             setTimeout(function(){
9205                 Roo.get(dom).removeClass("x-repaint");
9206             }, 1);
9207             return this;
9208         },
9209
9210         /**
9211          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9212          * then it returns the calculated width of the sides (see getPadding)
9213          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9214          * @return {Object/Number}
9215          */
9216         getMargins : function(side){
9217             if(!side){
9218                 return {
9219                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9220                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9221                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9222                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9223                 };
9224             }else{
9225                 return this.addStyles(side, El.margins);
9226              }
9227         },
9228
9229         // private
9230         addStyles : function(sides, styles){
9231             var val = 0, v, w;
9232             for(var i = 0, len = sides.length; i < len; i++){
9233                 v = this.getStyle(styles[sides.charAt(i)]);
9234                 if(v){
9235                      w = parseInt(v, 10);
9236                      if(w){ val += w; }
9237                 }
9238             }
9239             return val;
9240         },
9241
9242         /**
9243          * Creates a proxy element of this element
9244          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9245          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9246          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9247          * @return {Roo.Element} The new proxy element
9248          */
9249         createProxy : function(config, renderTo, matchBox){
9250             if(renderTo){
9251                 renderTo = Roo.getDom(renderTo);
9252             }else{
9253                 renderTo = document.body;
9254             }
9255             config = typeof config == "object" ?
9256                 config : {tag : "div", cls: config};
9257             var proxy = Roo.DomHelper.append(renderTo, config, true);
9258             if(matchBox){
9259                proxy.setBox(this.getBox());
9260             }
9261             return proxy;
9262         },
9263
9264         /**
9265          * Puts a mask over this element to disable user interaction. Requires core.css.
9266          * This method can only be applied to elements which accept child nodes.
9267          * @param {String} msg (optional) A message to display in the mask
9268          * @param {String} msgCls (optional) A css class to apply to the msg element
9269          * @return {Element} The mask  element
9270          */
9271         mask : function(msg, msgCls)
9272         {
9273             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9274                 this.setStyle("position", "relative");
9275             }
9276             if(!this._mask){
9277                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9278             }
9279             
9280             this.addClass("x-masked");
9281             this._mask.setDisplayed(true);
9282             
9283             // we wander
9284             var z = 0;
9285             var dom = this.dom;
9286             while (dom && dom.style) {
9287                 if (!isNaN(parseInt(dom.style.zIndex))) {
9288                     z = Math.max(z, parseInt(dom.style.zIndex));
9289                 }
9290                 dom = dom.parentNode;
9291             }
9292             // if we are masking the body - then it hides everything..
9293             if (this.dom == document.body) {
9294                 z = 1000000;
9295                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9296                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9297             }
9298            
9299             if(typeof msg == 'string'){
9300                 if(!this._maskMsg){
9301                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9302                         cls: "roo-el-mask-msg", 
9303                         cn: [
9304                             {
9305                                 tag: 'i',
9306                                 cls: 'fa fa-spinner fa-spin'
9307                             },
9308                             {
9309                                 tag: 'div'
9310                             }   
9311                         ]
9312                     }, true);
9313                 }
9314                 var mm = this._maskMsg;
9315                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9316                 if (mm.dom.lastChild) { // weird IE issue?
9317                     mm.dom.lastChild.innerHTML = msg;
9318                 }
9319                 mm.setDisplayed(true);
9320                 mm.center(this);
9321                 mm.setStyle('z-index', z + 102);
9322             }
9323             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9324                 this._mask.setHeight(this.getHeight());
9325             }
9326             this._mask.setStyle('z-index', z + 100);
9327             
9328             return this._mask;
9329         },
9330
9331         /**
9332          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9333          * it is cached for reuse.
9334          */
9335         unmask : function(removeEl){
9336             if(this._mask){
9337                 if(removeEl === true){
9338                     this._mask.remove();
9339                     delete this._mask;
9340                     if(this._maskMsg){
9341                         this._maskMsg.remove();
9342                         delete this._maskMsg;
9343                     }
9344                 }else{
9345                     this._mask.setDisplayed(false);
9346                     if(this._maskMsg){
9347                         this._maskMsg.setDisplayed(false);
9348                     }
9349                 }
9350             }
9351             this.removeClass("x-masked");
9352         },
9353
9354         /**
9355          * Returns true if this element is masked
9356          * @return {Boolean}
9357          */
9358         isMasked : function(){
9359             return this._mask && this._mask.isVisible();
9360         },
9361
9362         /**
9363          * Creates an iframe shim for this element to keep selects and other windowed objects from
9364          * showing through.
9365          * @return {Roo.Element} The new shim element
9366          */
9367         createShim : function(){
9368             var el = document.createElement('iframe');
9369             el.frameBorder = 'no';
9370             el.className = 'roo-shim';
9371             if(Roo.isIE && Roo.isSecure){
9372                 el.src = Roo.SSL_SECURE_URL;
9373             }
9374             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9375             shim.autoBoxAdjust = false;
9376             return shim;
9377         },
9378
9379         /**
9380          * Removes this element from the DOM and deletes it from the cache
9381          */
9382         remove : function(){
9383             if(this.dom.parentNode){
9384                 this.dom.parentNode.removeChild(this.dom);
9385             }
9386             delete El.cache[this.dom.id];
9387         },
9388
9389         /**
9390          * Sets up event handlers to add and remove a css class when the mouse is over this element
9391          * @param {String} className
9392          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9393          * mouseout events for children elements
9394          * @return {Roo.Element} this
9395          */
9396         addClassOnOver : function(className, preventFlicker){
9397             this.on("mouseover", function(){
9398                 Roo.fly(this, '_internal').addClass(className);
9399             }, this.dom);
9400             var removeFn = function(e){
9401                 if(preventFlicker !== true || !e.within(this, true)){
9402                     Roo.fly(this, '_internal').removeClass(className);
9403                 }
9404             };
9405             this.on("mouseout", removeFn, this.dom);
9406             return this;
9407         },
9408
9409         /**
9410          * Sets up event handlers to add and remove a css class when this element has the focus
9411          * @param {String} className
9412          * @return {Roo.Element} this
9413          */
9414         addClassOnFocus : function(className){
9415             this.on("focus", function(){
9416                 Roo.fly(this, '_internal').addClass(className);
9417             }, this.dom);
9418             this.on("blur", function(){
9419                 Roo.fly(this, '_internal').removeClass(className);
9420             }, this.dom);
9421             return this;
9422         },
9423         /**
9424          * 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)
9425          * @param {String} className
9426          * @return {Roo.Element} this
9427          */
9428         addClassOnClick : function(className){
9429             var dom = this.dom;
9430             this.on("mousedown", function(){
9431                 Roo.fly(dom, '_internal').addClass(className);
9432                 var d = Roo.get(document);
9433                 var fn = function(){
9434                     Roo.fly(dom, '_internal').removeClass(className);
9435                     d.removeListener("mouseup", fn);
9436                 };
9437                 d.on("mouseup", fn);
9438             });
9439             return this;
9440         },
9441
9442         /**
9443          * Stops the specified event from bubbling and optionally prevents the default action
9444          * @param {String} eventName
9445          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9446          * @return {Roo.Element} this
9447          */
9448         swallowEvent : function(eventName, preventDefault){
9449             var fn = function(e){
9450                 e.stopPropagation();
9451                 if(preventDefault){
9452                     e.preventDefault();
9453                 }
9454             };
9455             if(eventName instanceof Array){
9456                 for(var i = 0, len = eventName.length; i < len; i++){
9457                      this.on(eventName[i], fn);
9458                 }
9459                 return this;
9460             }
9461             this.on(eventName, fn);
9462             return this;
9463         },
9464
9465         /**
9466          * @private
9467          */
9468         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9469
9470         /**
9471          * Sizes this element to its parent element's dimensions performing
9472          * neccessary box adjustments.
9473          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9474          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9475          * @return {Roo.Element} this
9476          */
9477         fitToParent : function(monitorResize, targetParent) {
9478           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9479           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9480           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9481             return this;
9482           }
9483           var p = Roo.get(targetParent || this.dom.parentNode);
9484           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9485           if (monitorResize === true) {
9486             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9487             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9488           }
9489           return this;
9490         },
9491
9492         /**
9493          * Gets the next sibling, skipping text nodes
9494          * @return {HTMLElement} The next sibling or null
9495          */
9496         getNextSibling : function(){
9497             var n = this.dom.nextSibling;
9498             while(n && n.nodeType != 1){
9499                 n = n.nextSibling;
9500             }
9501             return n;
9502         },
9503
9504         /**
9505          * Gets the previous sibling, skipping text nodes
9506          * @return {HTMLElement} The previous sibling or null
9507          */
9508         getPrevSibling : function(){
9509             var n = this.dom.previousSibling;
9510             while(n && n.nodeType != 1){
9511                 n = n.previousSibling;
9512             }
9513             return n;
9514         },
9515
9516
9517         /**
9518          * Appends the passed element(s) to this element
9519          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9520          * @return {Roo.Element} this
9521          */
9522         appendChild: function(el){
9523             el = Roo.get(el);
9524             el.appendTo(this);
9525             return this;
9526         },
9527
9528         /**
9529          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9530          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9531          * automatically generated with the specified attributes.
9532          * @param {HTMLElement} insertBefore (optional) a child element of this element
9533          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9534          * @return {Roo.Element} The new child element
9535          */
9536         createChild: function(config, insertBefore, returnDom){
9537             config = config || {tag:'div'};
9538             if(insertBefore){
9539                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9540             }
9541             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9542         },
9543
9544         /**
9545          * Appends this element to the passed element
9546          * @param {String/HTMLElement/Element} el The new parent element
9547          * @return {Roo.Element} this
9548          */
9549         appendTo: function(el){
9550             el = Roo.getDom(el);
9551             el.appendChild(this.dom);
9552             return this;
9553         },
9554
9555         /**
9556          * Inserts this element before the passed element in the DOM
9557          * @param {String/HTMLElement/Element} el The element to insert before
9558          * @return {Roo.Element} this
9559          */
9560         insertBefore: function(el){
9561             el = Roo.getDom(el);
9562             el.parentNode.insertBefore(this.dom, el);
9563             return this;
9564         },
9565
9566         /**
9567          * Inserts this element after the passed element in the DOM
9568          * @param {String/HTMLElement/Element} el The element to insert after
9569          * @return {Roo.Element} this
9570          */
9571         insertAfter: function(el){
9572             el = Roo.getDom(el);
9573             el.parentNode.insertBefore(this.dom, el.nextSibling);
9574             return this;
9575         },
9576
9577         /**
9578          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9579          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9580          * @return {Roo.Element} The new child
9581          */
9582         insertFirst: function(el, returnDom){
9583             el = el || {};
9584             if(typeof el == 'object' && !el.nodeType){ // dh config
9585                 return this.createChild(el, this.dom.firstChild, returnDom);
9586             }else{
9587                 el = Roo.getDom(el);
9588                 this.dom.insertBefore(el, this.dom.firstChild);
9589                 return !returnDom ? Roo.get(el) : el;
9590             }
9591         },
9592
9593         /**
9594          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9595          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9596          * @param {String} where (optional) 'before' or 'after' defaults to before
9597          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9598          * @return {Roo.Element} the inserted Element
9599          */
9600         insertSibling: function(el, where, returnDom){
9601             where = where ? where.toLowerCase() : 'before';
9602             el = el || {};
9603             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9604
9605             if(typeof el == 'object' && !el.nodeType){ // dh config
9606                 if(where == 'after' && !this.dom.nextSibling){
9607                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9608                 }else{
9609                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9610                 }
9611
9612             }else{
9613                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9614                             where == 'before' ? this.dom : this.dom.nextSibling);
9615                 if(!returnDom){
9616                     rt = Roo.get(rt);
9617                 }
9618             }
9619             return rt;
9620         },
9621
9622         /**
9623          * Creates and wraps this element with another element
9624          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9625          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9626          * @return {HTMLElement/Element} The newly created wrapper element
9627          */
9628         wrap: function(config, returnDom){
9629             if(!config){
9630                 config = {tag: "div"};
9631             }
9632             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9633             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9634             return newEl;
9635         },
9636
9637         /**
9638          * Replaces the passed element with this element
9639          * @param {String/HTMLElement/Element} el The element to replace
9640          * @return {Roo.Element} this
9641          */
9642         replace: function(el){
9643             el = Roo.get(el);
9644             this.insertBefore(el);
9645             el.remove();
9646             return this;
9647         },
9648
9649         /**
9650          * Inserts an html fragment into this element
9651          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9652          * @param {String} html The HTML fragment
9653          * @param {Boolean} returnEl True to return an Roo.Element
9654          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9655          */
9656         insertHtml : function(where, html, returnEl){
9657             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9658             return returnEl ? Roo.get(el) : el;
9659         },
9660
9661         /**
9662          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9663          * @param {Object} o The object with the attributes
9664          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9665          * @return {Roo.Element} this
9666          */
9667         set : function(o, useSet){
9668             var el = this.dom;
9669             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9670             for(var attr in o){
9671                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9672                 if(attr=="cls"){
9673                     el.className = o["cls"];
9674                 }else{
9675                     if(useSet) {
9676                         el.setAttribute(attr, o[attr]);
9677                     } else {
9678                         el[attr] = o[attr];
9679                     }
9680                 }
9681             }
9682             if(o.style){
9683                 Roo.DomHelper.applyStyles(el, o.style);
9684             }
9685             return this;
9686         },
9687
9688         /**
9689          * Convenience method for constructing a KeyMap
9690          * @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:
9691          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9692          * @param {Function} fn The function to call
9693          * @param {Object} scope (optional) The scope of the function
9694          * @return {Roo.KeyMap} The KeyMap created
9695          */
9696         addKeyListener : function(key, fn, scope){
9697             var config;
9698             if(typeof key != "object" || key instanceof Array){
9699                 config = {
9700                     key: key,
9701                     fn: fn,
9702                     scope: scope
9703                 };
9704             }else{
9705                 config = {
9706                     key : key.key,
9707                     shift : key.shift,
9708                     ctrl : key.ctrl,
9709                     alt : key.alt,
9710                     fn: fn,
9711                     scope: scope
9712                 };
9713             }
9714             return new Roo.KeyMap(this, config);
9715         },
9716
9717         /**
9718          * Creates a KeyMap for this element
9719          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9720          * @return {Roo.KeyMap} The KeyMap created
9721          */
9722         addKeyMap : function(config){
9723             return new Roo.KeyMap(this, config);
9724         },
9725
9726         /**
9727          * Returns true if this element is scrollable.
9728          * @return {Boolean}
9729          */
9730          isScrollable : function(){
9731             var dom = this.dom;
9732             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9733         },
9734
9735         /**
9736          * 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().
9737          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9738          * @param {Number} value The new scroll value
9739          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9740          * @return {Element} this
9741          */
9742
9743         scrollTo : function(side, value, animate){
9744             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9745             if(!animate || !A){
9746                 this.dom[prop] = value;
9747             }else{
9748                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9749                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9750             }
9751             return this;
9752         },
9753
9754         /**
9755          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9756          * within this element's scrollable range.
9757          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9758          * @param {Number} distance How far to scroll the element in pixels
9759          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9760          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9761          * was scrolled as far as it could go.
9762          */
9763          scroll : function(direction, distance, animate){
9764              if(!this.isScrollable()){
9765                  return;
9766              }
9767              var el = this.dom;
9768              var l = el.scrollLeft, t = el.scrollTop;
9769              var w = el.scrollWidth, h = el.scrollHeight;
9770              var cw = el.clientWidth, ch = el.clientHeight;
9771              direction = direction.toLowerCase();
9772              var scrolled = false;
9773              var a = this.preanim(arguments, 2);
9774              switch(direction){
9775                  case "l":
9776                  case "left":
9777                      if(w - l > cw){
9778                          var v = Math.min(l + distance, w-cw);
9779                          this.scrollTo("left", v, a);
9780                          scrolled = true;
9781                      }
9782                      break;
9783                 case "r":
9784                 case "right":
9785                      if(l > 0){
9786                          var v = Math.max(l - distance, 0);
9787                          this.scrollTo("left", v, a);
9788                          scrolled = true;
9789                      }
9790                      break;
9791                 case "t":
9792                 case "top":
9793                 case "up":
9794                      if(t > 0){
9795                          var v = Math.max(t - distance, 0);
9796                          this.scrollTo("top", v, a);
9797                          scrolled = true;
9798                      }
9799                      break;
9800                 case "b":
9801                 case "bottom":
9802                 case "down":
9803                      if(h - t > ch){
9804                          var v = Math.min(t + distance, h-ch);
9805                          this.scrollTo("top", v, a);
9806                          scrolled = true;
9807                      }
9808                      break;
9809              }
9810              return scrolled;
9811         },
9812
9813         /**
9814          * Translates the passed page coordinates into left/top css values for this element
9815          * @param {Number/Array} x The page x or an array containing [x, y]
9816          * @param {Number} y The page y
9817          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9818          */
9819         translatePoints : function(x, y){
9820             if(typeof x == 'object' || x instanceof Array){
9821                 y = x[1]; x = x[0];
9822             }
9823             var p = this.getStyle('position');
9824             var o = this.getXY();
9825
9826             var l = parseInt(this.getStyle('left'), 10);
9827             var t = parseInt(this.getStyle('top'), 10);
9828
9829             if(isNaN(l)){
9830                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9831             }
9832             if(isNaN(t)){
9833                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9834             }
9835
9836             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9837         },
9838
9839         /**
9840          * Returns the current scroll position of the element.
9841          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9842          */
9843         getScroll : function(){
9844             var d = this.dom, doc = document;
9845             if(d == doc || d == doc.body){
9846                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9847                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9848                 return {left: l, top: t};
9849             }else{
9850                 return {left: d.scrollLeft, top: d.scrollTop};
9851             }
9852         },
9853
9854         /**
9855          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9856          * are convert to standard 6 digit hex color.
9857          * @param {String} attr The css attribute
9858          * @param {String} defaultValue The default value to use when a valid color isn't found
9859          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9860          * YUI color anims.
9861          */
9862         getColor : function(attr, defaultValue, prefix){
9863             var v = this.getStyle(attr);
9864             if(!v || v == "transparent" || v == "inherit") {
9865                 return defaultValue;
9866             }
9867             var color = typeof prefix == "undefined" ? "#" : prefix;
9868             if(v.substr(0, 4) == "rgb("){
9869                 var rvs = v.slice(4, v.length -1).split(",");
9870                 for(var i = 0; i < 3; i++){
9871                     var h = parseInt(rvs[i]).toString(16);
9872                     if(h < 16){
9873                         h = "0" + h;
9874                     }
9875                     color += h;
9876                 }
9877             } else {
9878                 if(v.substr(0, 1) == "#"){
9879                     if(v.length == 4) {
9880                         for(var i = 1; i < 4; i++){
9881                             var c = v.charAt(i);
9882                             color +=  c + c;
9883                         }
9884                     }else if(v.length == 7){
9885                         color += v.substr(1);
9886                     }
9887                 }
9888             }
9889             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9890         },
9891
9892         /**
9893          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9894          * gradient background, rounded corners and a 4-way shadow.
9895          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9896          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9897          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9898          * @return {Roo.Element} this
9899          */
9900         boxWrap : function(cls){
9901             cls = cls || 'x-box';
9902             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9903             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9904             return el;
9905         },
9906
9907         /**
9908          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9909          * @param {String} namespace The namespace in which to look for the attribute
9910          * @param {String} name The attribute name
9911          * @return {String} The attribute value
9912          */
9913         getAttributeNS : Roo.isIE ? function(ns, name){
9914             var d = this.dom;
9915             var type = typeof d[ns+":"+name];
9916             if(type != 'undefined' && type != 'unknown'){
9917                 return d[ns+":"+name];
9918             }
9919             return d[name];
9920         } : function(ns, name){
9921             var d = this.dom;
9922             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9923         },
9924         
9925         
9926         /**
9927          * Sets or Returns the value the dom attribute value
9928          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9929          * @param {String} value (optional) The value to set the attribute to
9930          * @return {String} The attribute value
9931          */
9932         attr : function(name){
9933             if (arguments.length > 1) {
9934                 this.dom.setAttribute(name, arguments[1]);
9935                 return arguments[1];
9936             }
9937             if (typeof(name) == 'object') {
9938                 for(var i in name) {
9939                     this.attr(i, name[i]);
9940                 }
9941                 return name;
9942             }
9943             
9944             
9945             if (!this.dom.hasAttribute(name)) {
9946                 return undefined;
9947             }
9948             return this.dom.getAttribute(name);
9949         }
9950         
9951         
9952         
9953     };
9954
9955     var ep = El.prototype;
9956
9957     /**
9958      * Appends an event handler (Shorthand for addListener)
9959      * @param {String}   eventName     The type of event to append
9960      * @param {Function} fn        The method the event invokes
9961      * @param {Object} scope       (optional) The scope (this object) of the fn
9962      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9963      * @method
9964      */
9965     ep.on = ep.addListener;
9966         // backwards compat
9967     ep.mon = ep.addListener;
9968
9969     /**
9970      * Removes an event handler from this element (shorthand for removeListener)
9971      * @param {String} eventName the type of event to remove
9972      * @param {Function} fn the method the event invokes
9973      * @return {Roo.Element} this
9974      * @method
9975      */
9976     ep.un = ep.removeListener;
9977
9978     /**
9979      * true to automatically adjust width and height settings for box-model issues (default to true)
9980      */
9981     ep.autoBoxAdjust = true;
9982
9983     // private
9984     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9985
9986     // private
9987     El.addUnits = function(v, defaultUnit){
9988         if(v === "" || v == "auto"){
9989             return v;
9990         }
9991         if(v === undefined){
9992             return '';
9993         }
9994         if(typeof v == "number" || !El.unitPattern.test(v)){
9995             return v + (defaultUnit || 'px');
9996         }
9997         return v;
9998     };
9999
10000     // special markup used throughout Roo when box wrapping elements
10001     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>';
10002     /**
10003      * Visibility mode constant - Use visibility to hide element
10004      * @static
10005      * @type Number
10006      */
10007     El.VISIBILITY = 1;
10008     /**
10009      * Visibility mode constant - Use display to hide element
10010      * @static
10011      * @type Number
10012      */
10013     El.DISPLAY = 2;
10014
10015     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10016     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10017     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10018
10019
10020
10021     /**
10022      * @private
10023      */
10024     El.cache = {};
10025
10026     var docEl;
10027
10028     /**
10029      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10030      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10031      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10032      * @return {Element} The Element object
10033      * @static
10034      */
10035     El.get = function(el){
10036         var ex, elm, id;
10037         if(!el){ return null; }
10038         if(typeof el == "string"){ // element id
10039             if(!(elm = document.getElementById(el))){
10040                 return null;
10041             }
10042             if(ex = El.cache[el]){
10043                 ex.dom = elm;
10044             }else{
10045                 ex = El.cache[el] = new El(elm);
10046             }
10047             return ex;
10048         }else if(el.tagName){ // dom element
10049             if(!(id = el.id)){
10050                 id = Roo.id(el);
10051             }
10052             if(ex = El.cache[id]){
10053                 ex.dom = el;
10054             }else{
10055                 ex = El.cache[id] = new El(el);
10056             }
10057             return ex;
10058         }else if(el instanceof El){
10059             if(el != docEl){
10060                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10061                                                               // catch case where it hasn't been appended
10062                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10063             }
10064             return el;
10065         }else if(el.isComposite){
10066             return el;
10067         }else if(el instanceof Array){
10068             return El.select(el);
10069         }else if(el == document){
10070             // create a bogus element object representing the document object
10071             if(!docEl){
10072                 var f = function(){};
10073                 f.prototype = El.prototype;
10074                 docEl = new f();
10075                 docEl.dom = document;
10076             }
10077             return docEl;
10078         }
10079         return null;
10080     };
10081
10082     // private
10083     El.uncache = function(el){
10084         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10085             if(a[i]){
10086                 delete El.cache[a[i].id || a[i]];
10087             }
10088         }
10089     };
10090
10091     // private
10092     // Garbage collection - uncache elements/purge listeners on orphaned elements
10093     // so we don't hold a reference and cause the browser to retain them
10094     El.garbageCollect = function(){
10095         if(!Roo.enableGarbageCollector){
10096             clearInterval(El.collectorThread);
10097             return;
10098         }
10099         for(var eid in El.cache){
10100             var el = El.cache[eid], d = el.dom;
10101             // -------------------------------------------------------
10102             // Determining what is garbage:
10103             // -------------------------------------------------------
10104             // !d
10105             // dom node is null, definitely garbage
10106             // -------------------------------------------------------
10107             // !d.parentNode
10108             // no parentNode == direct orphan, definitely garbage
10109             // -------------------------------------------------------
10110             // !d.offsetParent && !document.getElementById(eid)
10111             // display none elements have no offsetParent so we will
10112             // also try to look it up by it's id. However, check
10113             // offsetParent first so we don't do unneeded lookups.
10114             // This enables collection of elements that are not orphans
10115             // directly, but somewhere up the line they have an orphan
10116             // parent.
10117             // -------------------------------------------------------
10118             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10119                 delete El.cache[eid];
10120                 if(d && Roo.enableListenerCollection){
10121                     E.purgeElement(d);
10122                 }
10123             }
10124         }
10125     }
10126     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10127
10128
10129     // dom is optional
10130     El.Flyweight = function(dom){
10131         this.dom = dom;
10132     };
10133     El.Flyweight.prototype = El.prototype;
10134
10135     El._flyweights = {};
10136     /**
10137      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10138      * the dom node can be overwritten by other code.
10139      * @param {String/HTMLElement} el The dom node or id
10140      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10141      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10142      * @static
10143      * @return {Element} The shared Element object
10144      */
10145     El.fly = function(el, named){
10146         named = named || '_global';
10147         el = Roo.getDom(el);
10148         if(!el){
10149             return null;
10150         }
10151         if(!El._flyweights[named]){
10152             El._flyweights[named] = new El.Flyweight();
10153         }
10154         El._flyweights[named].dom = el;
10155         return El._flyweights[named];
10156     };
10157
10158     /**
10159      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10160      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10161      * Shorthand of {@link Roo.Element#get}
10162      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10163      * @return {Element} The Element object
10164      * @member Roo
10165      * @method get
10166      */
10167     Roo.get = El.get;
10168     /**
10169      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10170      * the dom node can be overwritten by other code.
10171      * Shorthand of {@link Roo.Element#fly}
10172      * @param {String/HTMLElement} el The dom node or id
10173      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10174      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10175      * @static
10176      * @return {Element} The shared Element object
10177      * @member Roo
10178      * @method fly
10179      */
10180     Roo.fly = El.fly;
10181
10182     // speedy lookup for elements never to box adjust
10183     var noBoxAdjust = Roo.isStrict ? {
10184         select:1
10185     } : {
10186         input:1, select:1, textarea:1
10187     };
10188     if(Roo.isIE || Roo.isGecko){
10189         noBoxAdjust['button'] = 1;
10190     }
10191
10192
10193     Roo.EventManager.on(window, 'unload', function(){
10194         delete El.cache;
10195         delete El._flyweights;
10196     });
10197 })();
10198
10199
10200
10201
10202 if(Roo.DomQuery){
10203     Roo.Element.selectorFunction = Roo.DomQuery.select;
10204 }
10205
10206 Roo.Element.select = function(selector, unique, root){
10207     var els;
10208     if(typeof selector == "string"){
10209         els = Roo.Element.selectorFunction(selector, root);
10210     }else if(selector.length !== undefined){
10211         els = selector;
10212     }else{
10213         throw "Invalid selector";
10214     }
10215     if(unique === true){
10216         return new Roo.CompositeElement(els);
10217     }else{
10218         return new Roo.CompositeElementLite(els);
10219     }
10220 };
10221 /**
10222  * Selects elements based on the passed CSS selector to enable working on them as 1.
10223  * @param {String/Array} selector The CSS selector or an array of elements
10224  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10225  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10226  * @return {CompositeElementLite/CompositeElement}
10227  * @member Roo
10228  * @method select
10229  */
10230 Roo.select = Roo.Element.select;
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245 /*
10246  * Based on:
10247  * Ext JS Library 1.1.1
10248  * Copyright(c) 2006-2007, Ext JS, LLC.
10249  *
10250  * Originally Released Under LGPL - original licence link has changed is not relivant.
10251  *
10252  * Fork - LGPL
10253  * <script type="text/javascript">
10254  */
10255
10256
10257
10258 //Notifies Element that fx methods are available
10259 Roo.enableFx = true;
10260
10261 /**
10262  * @class Roo.Fx
10263  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10264  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10265  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10266  * Element effects to work.</p><br/>
10267  *
10268  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10269  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10270  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10271  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10272  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10273  * expected results and should be done with care.</p><br/>
10274  *
10275  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10276  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10277 <pre>
10278 Value  Description
10279 -----  -----------------------------
10280 tl     The top left corner
10281 t      The center of the top edge
10282 tr     The top right corner
10283 l      The center of the left edge
10284 r      The center of the right edge
10285 bl     The bottom left corner
10286 b      The center of the bottom edge
10287 br     The bottom right corner
10288 </pre>
10289  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10290  * below are common options that can be passed to any Fx method.</b>
10291  * @cfg {Function} callback A function called when the effect is finished
10292  * @cfg {Object} scope The scope of the effect function
10293  * @cfg {String} easing A valid Easing value for the effect
10294  * @cfg {String} afterCls A css class to apply after the effect
10295  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10296  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10297  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10298  * effects that end with the element being visually hidden, ignored otherwise)
10299  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10300  * a function which returns such a specification that will be applied to the Element after the effect finishes
10301  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10302  * @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
10303  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10304  */
10305 Roo.Fx = {
10306         /**
10307          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10308          * origin for the slide effect.  This function automatically handles wrapping the element with
10309          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10310          * Usage:
10311          *<pre><code>
10312 // default: slide the element in from the top
10313 el.slideIn();
10314
10315 // custom: slide the element in from the right with a 2-second duration
10316 el.slideIn('r', { duration: 2 });
10317
10318 // common config options shown with default values
10319 el.slideIn('t', {
10320     easing: 'easeOut',
10321     duration: .5
10322 });
10323 </code></pre>
10324          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10325          * @param {Object} options (optional) Object literal with any of the Fx config options
10326          * @return {Roo.Element} The Element
10327          */
10328     slideIn : function(anchor, o){
10329         var el = this.getFxEl();
10330         o = o || {};
10331
10332         el.queueFx(o, function(){
10333
10334             anchor = anchor || "t";
10335
10336             // fix display to visibility
10337             this.fixDisplay();
10338
10339             // restore values after effect
10340             var r = this.getFxRestore();
10341             var b = this.getBox();
10342             // fixed size for slide
10343             this.setSize(b);
10344
10345             // wrap if needed
10346             var wrap = this.fxWrap(r.pos, o, "hidden");
10347
10348             var st = this.dom.style;
10349             st.visibility = "visible";
10350             st.position = "absolute";
10351
10352             // clear out temp styles after slide and unwrap
10353             var after = function(){
10354                 el.fxUnwrap(wrap, r.pos, o);
10355                 st.width = r.width;
10356                 st.height = r.height;
10357                 el.afterFx(o);
10358             };
10359             // time to calc the positions
10360             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10361
10362             switch(anchor.toLowerCase()){
10363                 case "t":
10364                     wrap.setSize(b.width, 0);
10365                     st.left = st.bottom = "0";
10366                     a = {height: bh};
10367                 break;
10368                 case "l":
10369                     wrap.setSize(0, b.height);
10370                     st.right = st.top = "0";
10371                     a = {width: bw};
10372                 break;
10373                 case "r":
10374                     wrap.setSize(0, b.height);
10375                     wrap.setX(b.right);
10376                     st.left = st.top = "0";
10377                     a = {width: bw, points: pt};
10378                 break;
10379                 case "b":
10380                     wrap.setSize(b.width, 0);
10381                     wrap.setY(b.bottom);
10382                     st.left = st.top = "0";
10383                     a = {height: bh, points: pt};
10384                 break;
10385                 case "tl":
10386                     wrap.setSize(0, 0);
10387                     st.right = st.bottom = "0";
10388                     a = {width: bw, height: bh};
10389                 break;
10390                 case "bl":
10391                     wrap.setSize(0, 0);
10392                     wrap.setY(b.y+b.height);
10393                     st.right = st.top = "0";
10394                     a = {width: bw, height: bh, points: pt};
10395                 break;
10396                 case "br":
10397                     wrap.setSize(0, 0);
10398                     wrap.setXY([b.right, b.bottom]);
10399                     st.left = st.top = "0";
10400                     a = {width: bw, height: bh, points: pt};
10401                 break;
10402                 case "tr":
10403                     wrap.setSize(0, 0);
10404                     wrap.setX(b.x+b.width);
10405                     st.left = st.bottom = "0";
10406                     a = {width: bw, height: bh, points: pt};
10407                 break;
10408             }
10409             this.dom.style.visibility = "visible";
10410             wrap.show();
10411
10412             arguments.callee.anim = wrap.fxanim(a,
10413                 o,
10414                 'motion',
10415                 .5,
10416                 'easeOut', after);
10417         });
10418         return this;
10419     },
10420     
10421         /**
10422          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10423          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10424          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10425          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10426          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10427          * Usage:
10428          *<pre><code>
10429 // default: slide the element out to the top
10430 el.slideOut();
10431
10432 // custom: slide the element out to the right with a 2-second duration
10433 el.slideOut('r', { duration: 2 });
10434
10435 // common config options shown with default values
10436 el.slideOut('t', {
10437     easing: 'easeOut',
10438     duration: .5,
10439     remove: false,
10440     useDisplay: false
10441 });
10442 </code></pre>
10443          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10444          * @param {Object} options (optional) Object literal with any of the Fx config options
10445          * @return {Roo.Element} The Element
10446          */
10447     slideOut : function(anchor, o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452
10453             anchor = anchor || "t";
10454
10455             // restore values after effect
10456             var r = this.getFxRestore();
10457             
10458             var b = this.getBox();
10459             // fixed size for slide
10460             this.setSize(b);
10461
10462             // wrap if needed
10463             var wrap = this.fxWrap(r.pos, o, "visible");
10464
10465             var st = this.dom.style;
10466             st.visibility = "visible";
10467             st.position = "absolute";
10468
10469             wrap.setSize(b);
10470
10471             var after = function(){
10472                 if(o.useDisplay){
10473                     el.setDisplayed(false);
10474                 }else{
10475                     el.hide();
10476                 }
10477
10478                 el.fxUnwrap(wrap, r.pos, o);
10479
10480                 st.width = r.width;
10481                 st.height = r.height;
10482
10483                 el.afterFx(o);
10484             };
10485
10486             var a, zero = {to: 0};
10487             switch(anchor.toLowerCase()){
10488                 case "t":
10489                     st.left = st.bottom = "0";
10490                     a = {height: zero};
10491                 break;
10492                 case "l":
10493                     st.right = st.top = "0";
10494                     a = {width: zero};
10495                 break;
10496                 case "r":
10497                     st.left = st.top = "0";
10498                     a = {width: zero, points: {to:[b.right, b.y]}};
10499                 break;
10500                 case "b":
10501                     st.left = st.top = "0";
10502                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10503                 break;
10504                 case "tl":
10505                     st.right = st.bottom = "0";
10506                     a = {width: zero, height: zero};
10507                 break;
10508                 case "bl":
10509                     st.right = st.top = "0";
10510                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10511                 break;
10512                 case "br":
10513                     st.left = st.top = "0";
10514                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10515                 break;
10516                 case "tr":
10517                     st.left = st.bottom = "0";
10518                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10519                 break;
10520             }
10521
10522             arguments.callee.anim = wrap.fxanim(a,
10523                 o,
10524                 'motion',
10525                 .5,
10526                 "easeOut", after);
10527         });
10528         return this;
10529     },
10530
10531         /**
10532          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10533          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10534          * The element must be removed from the DOM using the 'remove' config option if desired.
10535          * Usage:
10536          *<pre><code>
10537 // default
10538 el.puff();
10539
10540 // common config options shown with default values
10541 el.puff({
10542     easing: 'easeOut',
10543     duration: .5,
10544     remove: false,
10545     useDisplay: false
10546 });
10547 </code></pre>
10548          * @param {Object} options (optional) Object literal with any of the Fx config options
10549          * @return {Roo.Element} The Element
10550          */
10551     puff : function(o){
10552         var el = this.getFxEl();
10553         o = o || {};
10554
10555         el.queueFx(o, function(){
10556             this.clearOpacity();
10557             this.show();
10558
10559             // restore values after effect
10560             var r = this.getFxRestore();
10561             var st = this.dom.style;
10562
10563             var after = function(){
10564                 if(o.useDisplay){
10565                     el.setDisplayed(false);
10566                 }else{
10567                     el.hide();
10568                 }
10569
10570                 el.clearOpacity();
10571
10572                 el.setPositioning(r.pos);
10573                 st.width = r.width;
10574                 st.height = r.height;
10575                 st.fontSize = '';
10576                 el.afterFx(o);
10577             };
10578
10579             var width = this.getWidth();
10580             var height = this.getHeight();
10581
10582             arguments.callee.anim = this.fxanim({
10583                     width : {to: this.adjustWidth(width * 2)},
10584                     height : {to: this.adjustHeight(height * 2)},
10585                     points : {by: [-(width * .5), -(height * .5)]},
10586                     opacity : {to: 0},
10587                     fontSize: {to:200, unit: "%"}
10588                 },
10589                 o,
10590                 'motion',
10591                 .5,
10592                 "easeOut", after);
10593         });
10594         return this;
10595     },
10596
10597         /**
10598          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10599          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10600          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10601          * Usage:
10602          *<pre><code>
10603 // default
10604 el.switchOff();
10605
10606 // all config options shown with default values
10607 el.switchOff({
10608     easing: 'easeIn',
10609     duration: .3,
10610     remove: false,
10611     useDisplay: false
10612 });
10613 </code></pre>
10614          * @param {Object} options (optional) Object literal with any of the Fx config options
10615          * @return {Roo.Element} The Element
10616          */
10617     switchOff : function(o){
10618         var el = this.getFxEl();
10619         o = o || {};
10620
10621         el.queueFx(o, function(){
10622             this.clearOpacity();
10623             this.clip();
10624
10625             // restore values after effect
10626             var r = this.getFxRestore();
10627             var st = this.dom.style;
10628
10629             var after = function(){
10630                 if(o.useDisplay){
10631                     el.setDisplayed(false);
10632                 }else{
10633                     el.hide();
10634                 }
10635
10636                 el.clearOpacity();
10637                 el.setPositioning(r.pos);
10638                 st.width = r.width;
10639                 st.height = r.height;
10640
10641                 el.afterFx(o);
10642             };
10643
10644             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10645                 this.clearOpacity();
10646                 (function(){
10647                     this.fxanim({
10648                         height:{to:1},
10649                         points:{by:[0, this.getHeight() * .5]}
10650                     }, o, 'motion', 0.3, 'easeIn', after);
10651                 }).defer(100, this);
10652             });
10653         });
10654         return this;
10655     },
10656
10657     /**
10658      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10659      * changed using the "attr" config option) and then fading back to the original color. If no original
10660      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10661      * Usage:
10662 <pre><code>
10663 // default: highlight background to yellow
10664 el.highlight();
10665
10666 // custom: highlight foreground text to blue for 2 seconds
10667 el.highlight("0000ff", { attr: 'color', duration: 2 });
10668
10669 // common config options shown with default values
10670 el.highlight("ffff9c", {
10671     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10672     endColor: (current color) or "ffffff",
10673     easing: 'easeIn',
10674     duration: 1
10675 });
10676 </code></pre>
10677      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10678      * @param {Object} options (optional) Object literal with any of the Fx config options
10679      * @return {Roo.Element} The Element
10680      */ 
10681     highlight : function(color, o){
10682         var el = this.getFxEl();
10683         o = o || {};
10684
10685         el.queueFx(o, function(){
10686             color = color || "ffff9c";
10687             attr = o.attr || "backgroundColor";
10688
10689             this.clearOpacity();
10690             this.show();
10691
10692             var origColor = this.getColor(attr);
10693             var restoreColor = this.dom.style[attr];
10694             endColor = (o.endColor || origColor) || "ffffff";
10695
10696             var after = function(){
10697                 el.dom.style[attr] = restoreColor;
10698                 el.afterFx(o);
10699             };
10700
10701             var a = {};
10702             a[attr] = {from: color, to: endColor};
10703             arguments.callee.anim = this.fxanim(a,
10704                 o,
10705                 'color',
10706                 1,
10707                 'easeIn', after);
10708         });
10709         return this;
10710     },
10711
10712    /**
10713     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10714     * Usage:
10715 <pre><code>
10716 // default: a single light blue ripple
10717 el.frame();
10718
10719 // custom: 3 red ripples lasting 3 seconds total
10720 el.frame("ff0000", 3, { duration: 3 });
10721
10722 // common config options shown with default values
10723 el.frame("C3DAF9", 1, {
10724     duration: 1 //duration of entire animation (not each individual ripple)
10725     // Note: Easing is not configurable and will be ignored if included
10726 });
10727 </code></pre>
10728     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10729     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10730     * @param {Object} options (optional) Object literal with any of the Fx config options
10731     * @return {Roo.Element} The Element
10732     */
10733     frame : function(color, count, o){
10734         var el = this.getFxEl();
10735         o = o || {};
10736
10737         el.queueFx(o, function(){
10738             color = color || "#C3DAF9";
10739             if(color.length == 6){
10740                 color = "#" + color;
10741             }
10742             count = count || 1;
10743             duration = o.duration || 1;
10744             this.show();
10745
10746             var b = this.getBox();
10747             var animFn = function(){
10748                 var proxy = this.createProxy({
10749
10750                      style:{
10751                         visbility:"hidden",
10752                         position:"absolute",
10753                         "z-index":"35000", // yee haw
10754                         border:"0px solid " + color
10755                      }
10756                   });
10757                 var scale = Roo.isBorderBox ? 2 : 1;
10758                 proxy.animate({
10759                     top:{from:b.y, to:b.y - 20},
10760                     left:{from:b.x, to:b.x - 20},
10761                     borderWidth:{from:0, to:10},
10762                     opacity:{from:1, to:0},
10763                     height:{from:b.height, to:(b.height + (20*scale))},
10764                     width:{from:b.width, to:(b.width + (20*scale))}
10765                 }, duration, function(){
10766                     proxy.remove();
10767                 });
10768                 if(--count > 0){
10769                      animFn.defer((duration/2)*1000, this);
10770                 }else{
10771                     el.afterFx(o);
10772                 }
10773             };
10774             animFn.call(this);
10775         });
10776         return this;
10777     },
10778
10779    /**
10780     * Creates a pause before any subsequent queued effects begin.  If there are
10781     * no effects queued after the pause it will have no effect.
10782     * Usage:
10783 <pre><code>
10784 el.pause(1);
10785 </code></pre>
10786     * @param {Number} seconds The length of time to pause (in seconds)
10787     * @return {Roo.Element} The Element
10788     */
10789     pause : function(seconds){
10790         var el = this.getFxEl();
10791         var o = {};
10792
10793         el.queueFx(o, function(){
10794             setTimeout(function(){
10795                 el.afterFx(o);
10796             }, seconds * 1000);
10797         });
10798         return this;
10799     },
10800
10801    /**
10802     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10803     * using the "endOpacity" config option.
10804     * Usage:
10805 <pre><code>
10806 // default: fade in from opacity 0 to 100%
10807 el.fadeIn();
10808
10809 // custom: fade in from opacity 0 to 75% over 2 seconds
10810 el.fadeIn({ endOpacity: .75, duration: 2});
10811
10812 // common config options shown with default values
10813 el.fadeIn({
10814     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10815     easing: 'easeOut',
10816     duration: .5
10817 });
10818 </code></pre>
10819     * @param {Object} options (optional) Object literal with any of the Fx config options
10820     * @return {Roo.Element} The Element
10821     */
10822     fadeIn : function(o){
10823         var el = this.getFxEl();
10824         o = o || {};
10825         el.queueFx(o, function(){
10826             this.setOpacity(0);
10827             this.fixDisplay();
10828             this.dom.style.visibility = 'visible';
10829             var to = o.endOpacity || 1;
10830             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10831                 o, null, .5, "easeOut", function(){
10832                 if(to == 1){
10833                     this.clearOpacity();
10834                 }
10835                 el.afterFx(o);
10836             });
10837         });
10838         return this;
10839     },
10840
10841    /**
10842     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10843     * using the "endOpacity" config option.
10844     * Usage:
10845 <pre><code>
10846 // default: fade out from the element's current opacity to 0
10847 el.fadeOut();
10848
10849 // custom: fade out from the element's current opacity to 25% over 2 seconds
10850 el.fadeOut({ endOpacity: .25, duration: 2});
10851
10852 // common config options shown with default values
10853 el.fadeOut({
10854     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10855     easing: 'easeOut',
10856     duration: .5
10857     remove: false,
10858     useDisplay: false
10859 });
10860 </code></pre>
10861     * @param {Object} options (optional) Object literal with any of the Fx config options
10862     * @return {Roo.Element} The Element
10863     */
10864     fadeOut : function(o){
10865         var el = this.getFxEl();
10866         o = o || {};
10867         el.queueFx(o, function(){
10868             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10869                 o, null, .5, "easeOut", function(){
10870                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10871                      this.dom.style.display = "none";
10872                 }else{
10873                      this.dom.style.visibility = "hidden";
10874                 }
10875                 this.clearOpacity();
10876                 el.afterFx(o);
10877             });
10878         });
10879         return this;
10880     },
10881
10882    /**
10883     * Animates the transition of an element's dimensions from a starting height/width
10884     * to an ending height/width.
10885     * Usage:
10886 <pre><code>
10887 // change height and width to 100x100 pixels
10888 el.scale(100, 100);
10889
10890 // common config options shown with default values.  The height and width will default to
10891 // the element's existing values if passed as null.
10892 el.scale(
10893     [element's width],
10894     [element's height], {
10895     easing: 'easeOut',
10896     duration: .35
10897 });
10898 </code></pre>
10899     * @param {Number} width  The new width (pass undefined to keep the original width)
10900     * @param {Number} height  The new height (pass undefined to keep the original height)
10901     * @param {Object} options (optional) Object literal with any of the Fx config options
10902     * @return {Roo.Element} The Element
10903     */
10904     scale : function(w, h, o){
10905         this.shift(Roo.apply({}, o, {
10906             width: w,
10907             height: h
10908         }));
10909         return this;
10910     },
10911
10912    /**
10913     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10914     * Any of these properties not specified in the config object will not be changed.  This effect 
10915     * requires that at least one new dimension, position or opacity setting must be passed in on
10916     * the config object in order for the function to have any effect.
10917     * Usage:
10918 <pre><code>
10919 // slide the element horizontally to x position 200 while changing the height and opacity
10920 el.shift({ x: 200, height: 50, opacity: .8 });
10921
10922 // common config options shown with default values.
10923 el.shift({
10924     width: [element's width],
10925     height: [element's height],
10926     x: [element's x position],
10927     y: [element's y position],
10928     opacity: [element's opacity],
10929     easing: 'easeOut',
10930     duration: .35
10931 });
10932 </code></pre>
10933     * @param {Object} options  Object literal with any of the Fx config options
10934     * @return {Roo.Element} The Element
10935     */
10936     shift : function(o){
10937         var el = this.getFxEl();
10938         o = o || {};
10939         el.queueFx(o, function(){
10940             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10941             if(w !== undefined){
10942                 a.width = {to: this.adjustWidth(w)};
10943             }
10944             if(h !== undefined){
10945                 a.height = {to: this.adjustHeight(h)};
10946             }
10947             if(x !== undefined || y !== undefined){
10948                 a.points = {to: [
10949                     x !== undefined ? x : this.getX(),
10950                     y !== undefined ? y : this.getY()
10951                 ]};
10952             }
10953             if(op !== undefined){
10954                 a.opacity = {to: op};
10955             }
10956             if(o.xy !== undefined){
10957                 a.points = {to: o.xy};
10958             }
10959             arguments.callee.anim = this.fxanim(a,
10960                 o, 'motion', .35, "easeOut", function(){
10961                 el.afterFx(o);
10962             });
10963         });
10964         return this;
10965     },
10966
10967         /**
10968          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10969          * ending point of the effect.
10970          * Usage:
10971          *<pre><code>
10972 // default: slide the element downward while fading out
10973 el.ghost();
10974
10975 // custom: slide the element out to the right with a 2-second duration
10976 el.ghost('r', { duration: 2 });
10977
10978 // common config options shown with default values
10979 el.ghost('b', {
10980     easing: 'easeOut',
10981     duration: .5
10982     remove: false,
10983     useDisplay: false
10984 });
10985 </code></pre>
10986          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10987          * @param {Object} options (optional) Object literal with any of the Fx config options
10988          * @return {Roo.Element} The Element
10989          */
10990     ghost : function(anchor, o){
10991         var el = this.getFxEl();
10992         o = o || {};
10993
10994         el.queueFx(o, function(){
10995             anchor = anchor || "b";
10996
10997             // restore values after effect
10998             var r = this.getFxRestore();
10999             var w = this.getWidth(),
11000                 h = this.getHeight();
11001
11002             var st = this.dom.style;
11003
11004             var after = function(){
11005                 if(o.useDisplay){
11006                     el.setDisplayed(false);
11007                 }else{
11008                     el.hide();
11009                 }
11010
11011                 el.clearOpacity();
11012                 el.setPositioning(r.pos);
11013                 st.width = r.width;
11014                 st.height = r.height;
11015
11016                 el.afterFx(o);
11017             };
11018
11019             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11020             switch(anchor.toLowerCase()){
11021                 case "t":
11022                     pt.by = [0, -h];
11023                 break;
11024                 case "l":
11025                     pt.by = [-w, 0];
11026                 break;
11027                 case "r":
11028                     pt.by = [w, 0];
11029                 break;
11030                 case "b":
11031                     pt.by = [0, h];
11032                 break;
11033                 case "tl":
11034                     pt.by = [-w, -h];
11035                 break;
11036                 case "bl":
11037                     pt.by = [-w, h];
11038                 break;
11039                 case "br":
11040                     pt.by = [w, h];
11041                 break;
11042                 case "tr":
11043                     pt.by = [w, -h];
11044                 break;
11045             }
11046
11047             arguments.callee.anim = this.fxanim(a,
11048                 o,
11049                 'motion',
11050                 .5,
11051                 "easeOut", after);
11052         });
11053         return this;
11054     },
11055
11056         /**
11057          * Ensures that all effects queued after syncFx is called on the element are
11058          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11059          * @return {Roo.Element} The Element
11060          */
11061     syncFx : function(){
11062         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11063             block : false,
11064             concurrent : true,
11065             stopFx : false
11066         });
11067         return this;
11068     },
11069
11070         /**
11071          * Ensures that all effects queued after sequenceFx is called on the element are
11072          * run in sequence.  This is the opposite of {@link #syncFx}.
11073          * @return {Roo.Element} The Element
11074          */
11075     sequenceFx : function(){
11076         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11077             block : false,
11078             concurrent : false,
11079             stopFx : false
11080         });
11081         return this;
11082     },
11083
11084         /* @private */
11085     nextFx : function(){
11086         var ef = this.fxQueue[0];
11087         if(ef){
11088             ef.call(this);
11089         }
11090     },
11091
11092         /**
11093          * Returns true if the element has any effects actively running or queued, else returns false.
11094          * @return {Boolean} True if element has active effects, else false
11095          */
11096     hasActiveFx : function(){
11097         return this.fxQueue && this.fxQueue[0];
11098     },
11099
11100         /**
11101          * Stops any running effects and clears the element's internal effects queue if it contains
11102          * any additional effects that haven't started yet.
11103          * @return {Roo.Element} The Element
11104          */
11105     stopFx : function(){
11106         if(this.hasActiveFx()){
11107             var cur = this.fxQueue[0];
11108             if(cur && cur.anim && cur.anim.isAnimated()){
11109                 this.fxQueue = [cur]; // clear out others
11110                 cur.anim.stop(true);
11111             }
11112         }
11113         return this;
11114     },
11115
11116         /* @private */
11117     beforeFx : function(o){
11118         if(this.hasActiveFx() && !o.concurrent){
11119            if(o.stopFx){
11120                this.stopFx();
11121                return true;
11122            }
11123            return false;
11124         }
11125         return true;
11126     },
11127
11128         /**
11129          * Returns true if the element is currently blocking so that no other effect can be queued
11130          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11131          * used to ensure that an effect initiated by a user action runs to completion prior to the
11132          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11133          * @return {Boolean} True if blocking, else false
11134          */
11135     hasFxBlock : function(){
11136         var q = this.fxQueue;
11137         return q && q[0] && q[0].block;
11138     },
11139
11140         /* @private */
11141     queueFx : function(o, fn){
11142         if(!this.fxQueue){
11143             this.fxQueue = [];
11144         }
11145         if(!this.hasFxBlock()){
11146             Roo.applyIf(o, this.fxDefaults);
11147             if(!o.concurrent){
11148                 var run = this.beforeFx(o);
11149                 fn.block = o.block;
11150                 this.fxQueue.push(fn);
11151                 if(run){
11152                     this.nextFx();
11153                 }
11154             }else{
11155                 fn.call(this);
11156             }
11157         }
11158         return this;
11159     },
11160
11161         /* @private */
11162     fxWrap : function(pos, o, vis){
11163         var wrap;
11164         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11165             var wrapXY;
11166             if(o.fixPosition){
11167                 wrapXY = this.getXY();
11168             }
11169             var div = document.createElement("div");
11170             div.style.visibility = vis;
11171             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11172             wrap.setPositioning(pos);
11173             if(wrap.getStyle("position") == "static"){
11174                 wrap.position("relative");
11175             }
11176             this.clearPositioning('auto');
11177             wrap.clip();
11178             wrap.dom.appendChild(this.dom);
11179             if(wrapXY){
11180                 wrap.setXY(wrapXY);
11181             }
11182         }
11183         return wrap;
11184     },
11185
11186         /* @private */
11187     fxUnwrap : function(wrap, pos, o){
11188         this.clearPositioning();
11189         this.setPositioning(pos);
11190         if(!o.wrap){
11191             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11192             wrap.remove();
11193         }
11194     },
11195
11196         /* @private */
11197     getFxRestore : function(){
11198         var st = this.dom.style;
11199         return {pos: this.getPositioning(), width: st.width, height : st.height};
11200     },
11201
11202         /* @private */
11203     afterFx : function(o){
11204         if(o.afterStyle){
11205             this.applyStyles(o.afterStyle);
11206         }
11207         if(o.afterCls){
11208             this.addClass(o.afterCls);
11209         }
11210         if(o.remove === true){
11211             this.remove();
11212         }
11213         Roo.callback(o.callback, o.scope, [this]);
11214         if(!o.concurrent){
11215             this.fxQueue.shift();
11216             this.nextFx();
11217         }
11218     },
11219
11220         /* @private */
11221     getFxEl : function(){ // support for composite element fx
11222         return Roo.get(this.dom);
11223     },
11224
11225         /* @private */
11226     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11227         animType = animType || 'run';
11228         opt = opt || {};
11229         var anim = Roo.lib.Anim[animType](
11230             this.dom, args,
11231             (opt.duration || defaultDur) || .35,
11232             (opt.easing || defaultEase) || 'easeOut',
11233             function(){
11234                 Roo.callback(cb, this);
11235             },
11236             this
11237         );
11238         opt.anim = anim;
11239         return anim;
11240     }
11241 };
11242
11243 // backwords compat
11244 Roo.Fx.resize = Roo.Fx.scale;
11245
11246 //When included, Roo.Fx is automatically applied to Element so that all basic
11247 //effects are available directly via the Element API
11248 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11249  * Based on:
11250  * Ext JS Library 1.1.1
11251  * Copyright(c) 2006-2007, Ext JS, LLC.
11252  *
11253  * Originally Released Under LGPL - original licence link has changed is not relivant.
11254  *
11255  * Fork - LGPL
11256  * <script type="text/javascript">
11257  */
11258
11259
11260 /**
11261  * @class Roo.CompositeElement
11262  * Standard composite class. Creates a Roo.Element for every element in the collection.
11263  * <br><br>
11264  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11265  * actions will be performed on all the elements in this collection.</b>
11266  * <br><br>
11267  * All methods return <i>this</i> and can be chained.
11268  <pre><code>
11269  var els = Roo.select("#some-el div.some-class", true);
11270  // or select directly from an existing element
11271  var el = Roo.get('some-el');
11272  el.select('div.some-class', true);
11273
11274  els.setWidth(100); // all elements become 100 width
11275  els.hide(true); // all elements fade out and hide
11276  // or
11277  els.setWidth(100).hide(true);
11278  </code></pre>
11279  */
11280 Roo.CompositeElement = function(els){
11281     this.elements = [];
11282     this.addElements(els);
11283 };
11284 Roo.CompositeElement.prototype = {
11285     isComposite: true,
11286     addElements : function(els){
11287         if(!els) {
11288             return this;
11289         }
11290         if(typeof els == "string"){
11291             els = Roo.Element.selectorFunction(els);
11292         }
11293         var yels = this.elements;
11294         var index = yels.length-1;
11295         for(var i = 0, len = els.length; i < len; i++) {
11296                 yels[++index] = Roo.get(els[i]);
11297         }
11298         return this;
11299     },
11300
11301     /**
11302     * Clears this composite and adds the elements returned by the passed selector.
11303     * @param {String/Array} els A string CSS selector, an array of elements or an element
11304     * @return {CompositeElement} this
11305     */
11306     fill : function(els){
11307         this.elements = [];
11308         this.add(els);
11309         return this;
11310     },
11311
11312     /**
11313     * Filters this composite to only elements that match the passed selector.
11314     * @param {String} selector A string CSS selector
11315     * @param {Boolean} inverse return inverse filter (not matches)
11316     * @return {CompositeElement} this
11317     */
11318     filter : function(selector, inverse){
11319         var els = [];
11320         inverse = inverse || false;
11321         this.each(function(el){
11322             var match = inverse ? !el.is(selector) : el.is(selector);
11323             if(match){
11324                 els[els.length] = el.dom;
11325             }
11326         });
11327         this.fill(els);
11328         return this;
11329     },
11330
11331     invoke : function(fn, args){
11332         var els = this.elements;
11333         for(var i = 0, len = els.length; i < len; i++) {
11334                 Roo.Element.prototype[fn].apply(els[i], args);
11335         }
11336         return this;
11337     },
11338     /**
11339     * Adds elements to this composite.
11340     * @param {String/Array} els A string CSS selector, an array of elements or an element
11341     * @return {CompositeElement} this
11342     */
11343     add : function(els){
11344         if(typeof els == "string"){
11345             this.addElements(Roo.Element.selectorFunction(els));
11346         }else if(els.length !== undefined){
11347             this.addElements(els);
11348         }else{
11349             this.addElements([els]);
11350         }
11351         return this;
11352     },
11353     /**
11354     * Calls the passed function passing (el, this, index) for each element in this composite.
11355     * @param {Function} fn The function to call
11356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11357     * @return {CompositeElement} this
11358     */
11359     each : function(fn, scope){
11360         var els = this.elements;
11361         for(var i = 0, len = els.length; i < len; i++){
11362             if(fn.call(scope || els[i], els[i], this, i) === false) {
11363                 break;
11364             }
11365         }
11366         return this;
11367     },
11368
11369     /**
11370      * Returns the Element object at the specified index
11371      * @param {Number} index
11372      * @return {Roo.Element}
11373      */
11374     item : function(index){
11375         return this.elements[index] || null;
11376     },
11377
11378     /**
11379      * Returns the first Element
11380      * @return {Roo.Element}
11381      */
11382     first : function(){
11383         return this.item(0);
11384     },
11385
11386     /**
11387      * Returns the last Element
11388      * @return {Roo.Element}
11389      */
11390     last : function(){
11391         return this.item(this.elements.length-1);
11392     },
11393
11394     /**
11395      * Returns the number of elements in this composite
11396      * @return Number
11397      */
11398     getCount : function(){
11399         return this.elements.length;
11400     },
11401
11402     /**
11403      * Returns true if this composite contains the passed element
11404      * @return Boolean
11405      */
11406     contains : function(el){
11407         return this.indexOf(el) !== -1;
11408     },
11409
11410     /**
11411      * Returns true if this composite contains the passed element
11412      * @return Boolean
11413      */
11414     indexOf : function(el){
11415         return this.elements.indexOf(Roo.get(el));
11416     },
11417
11418
11419     /**
11420     * Removes the specified element(s).
11421     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11422     * or an array of any of those.
11423     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11424     * @return {CompositeElement} this
11425     */
11426     removeElement : function(el, removeDom){
11427         if(el instanceof Array){
11428             for(var i = 0, len = el.length; i < len; i++){
11429                 this.removeElement(el[i]);
11430             }
11431             return this;
11432         }
11433         var index = typeof el == 'number' ? el : this.indexOf(el);
11434         if(index !== -1){
11435             if(removeDom){
11436                 var d = this.elements[index];
11437                 if(d.dom){
11438                     d.remove();
11439                 }else{
11440                     d.parentNode.removeChild(d);
11441                 }
11442             }
11443             this.elements.splice(index, 1);
11444         }
11445         return this;
11446     },
11447
11448     /**
11449     * Replaces the specified element with the passed element.
11450     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11451     * to replace.
11452     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11453     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11454     * @return {CompositeElement} this
11455     */
11456     replaceElement : function(el, replacement, domReplace){
11457         var index = typeof el == 'number' ? el : this.indexOf(el);
11458         if(index !== -1){
11459             if(domReplace){
11460                 this.elements[index].replaceWith(replacement);
11461             }else{
11462                 this.elements.splice(index, 1, Roo.get(replacement))
11463             }
11464         }
11465         return this;
11466     },
11467
11468     /**
11469      * Removes all elements.
11470      */
11471     clear : function(){
11472         this.elements = [];
11473     }
11474 };
11475 (function(){
11476     Roo.CompositeElement.createCall = function(proto, fnName){
11477         if(!proto[fnName]){
11478             proto[fnName] = function(){
11479                 return this.invoke(fnName, arguments);
11480             };
11481         }
11482     };
11483     for(var fnName in Roo.Element.prototype){
11484         if(typeof Roo.Element.prototype[fnName] == "function"){
11485             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11486         }
11487     };
11488 })();
11489 /*
11490  * Based on:
11491  * Ext JS Library 1.1.1
11492  * Copyright(c) 2006-2007, Ext JS, LLC.
11493  *
11494  * Originally Released Under LGPL - original licence link has changed is not relivant.
11495  *
11496  * Fork - LGPL
11497  * <script type="text/javascript">
11498  */
11499
11500 /**
11501  * @class Roo.CompositeElementLite
11502  * @extends Roo.CompositeElement
11503  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11504  <pre><code>
11505  var els = Roo.select("#some-el div.some-class");
11506  // or select directly from an existing element
11507  var el = Roo.get('some-el');
11508  el.select('div.some-class');
11509
11510  els.setWidth(100); // all elements become 100 width
11511  els.hide(true); // all elements fade out and hide
11512  // or
11513  els.setWidth(100).hide(true);
11514  </code></pre><br><br>
11515  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11516  * actions will be performed on all the elements in this collection.</b>
11517  */
11518 Roo.CompositeElementLite = function(els){
11519     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11520     this.el = new Roo.Element.Flyweight();
11521 };
11522 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11523     addElements : function(els){
11524         if(els){
11525             if(els instanceof Array){
11526                 this.elements = this.elements.concat(els);
11527             }else{
11528                 var yels = this.elements;
11529                 var index = yels.length-1;
11530                 for(var i = 0, len = els.length; i < len; i++) {
11531                     yels[++index] = els[i];
11532                 }
11533             }
11534         }
11535         return this;
11536     },
11537     invoke : function(fn, args){
11538         var els = this.elements;
11539         var el = this.el;
11540         for(var i = 0, len = els.length; i < len; i++) {
11541             el.dom = els[i];
11542                 Roo.Element.prototype[fn].apply(el, args);
11543         }
11544         return this;
11545     },
11546     /**
11547      * Returns a flyweight Element of the dom element object at the specified index
11548      * @param {Number} index
11549      * @return {Roo.Element}
11550      */
11551     item : function(index){
11552         if(!this.elements[index]){
11553             return null;
11554         }
11555         this.el.dom = this.elements[index];
11556         return this.el;
11557     },
11558
11559     // fixes scope with flyweight
11560     addListener : function(eventName, handler, scope, opt){
11561         var els = this.elements;
11562         for(var i = 0, len = els.length; i < len; i++) {
11563             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11564         }
11565         return this;
11566     },
11567
11568     /**
11569     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11570     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11571     * a reference to the dom node, use el.dom.</b>
11572     * @param {Function} fn The function to call
11573     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11574     * @return {CompositeElement} this
11575     */
11576     each : function(fn, scope){
11577         var els = this.elements;
11578         var el = this.el;
11579         for(var i = 0, len = els.length; i < len; i++){
11580             el.dom = els[i];
11581                 if(fn.call(scope || el, el, this, i) === false){
11582                 break;
11583             }
11584         }
11585         return this;
11586     },
11587
11588     indexOf : function(el){
11589         return this.elements.indexOf(Roo.getDom(el));
11590     },
11591
11592     replaceElement : function(el, replacement, domReplace){
11593         var index = typeof el == 'number' ? el : this.indexOf(el);
11594         if(index !== -1){
11595             replacement = Roo.getDom(replacement);
11596             if(domReplace){
11597                 var d = this.elements[index];
11598                 d.parentNode.insertBefore(replacement, d);
11599                 d.parentNode.removeChild(d);
11600             }
11601             this.elements.splice(index, 1, replacement);
11602         }
11603         return this;
11604     }
11605 });
11606 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11607
11608 /*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620
11621 /**
11622  * @class Roo.data.Connection
11623  * @extends Roo.util.Observable
11624  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11625  * either to a configured URL, or to a URL specified at request time. 
11626  * 
11627  * Requests made by this class are asynchronous, and will return immediately. No data from
11628  * the server will be available to the statement immediately following the {@link #request} call.
11629  * To process returned data, use a callback in the request options object, or an event listener.
11630  * 
11631  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11632  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11633  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11634  * property and, if present, the IFRAME's XML document as the responseXML property.
11635  * 
11636  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11637  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11638  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11639  * standard DOM methods.
11640  * @constructor
11641  * @param {Object} config a configuration object.
11642  */
11643 Roo.data.Connection = function(config){
11644     Roo.apply(this, config);
11645     this.addEvents({
11646         /**
11647          * @event beforerequest
11648          * Fires before a network request is made to retrieve a data object.
11649          * @param {Connection} conn This Connection object.
11650          * @param {Object} options The options config object passed to the {@link #request} method.
11651          */
11652         "beforerequest" : true,
11653         /**
11654          * @event requestcomplete
11655          * Fires if the request was successfully completed.
11656          * @param {Connection} conn This Connection object.
11657          * @param {Object} response The XHR object containing the response data.
11658          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11659          * @param {Object} options The options config object passed to the {@link #request} method.
11660          */
11661         "requestcomplete" : true,
11662         /**
11663          * @event requestexception
11664          * Fires if an error HTTP status was returned from the server.
11665          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11666          * @param {Connection} conn This Connection object.
11667          * @param {Object} response The XHR object containing the response data.
11668          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11669          * @param {Object} options The options config object passed to the {@link #request} method.
11670          */
11671         "requestexception" : true
11672     });
11673     Roo.data.Connection.superclass.constructor.call(this);
11674 };
11675
11676 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11677     /**
11678      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11679      */
11680     /**
11681      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11682      * extra parameters to each request made by this object. (defaults to undefined)
11683      */
11684     /**
11685      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11686      *  to each request made by this object. (defaults to undefined)
11687      */
11688     /**
11689      * @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)
11690      */
11691     /**
11692      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11693      */
11694     timeout : 30000,
11695     /**
11696      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11697      * @type Boolean
11698      */
11699     autoAbort:false,
11700
11701     /**
11702      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11703      * @type Boolean
11704      */
11705     disableCaching: true,
11706
11707     /**
11708      * Sends an HTTP request to a remote server.
11709      * @param {Object} options An object which may contain the following properties:<ul>
11710      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11711      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11712      * request, a url encoded string or a function to call to get either.</li>
11713      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11714      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11715      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11716      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11717      * <li>options {Object} The parameter to the request call.</li>
11718      * <li>success {Boolean} True if the request succeeded.</li>
11719      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11720      * </ul></li>
11721      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11722      * The callback is passed the following parameters:<ul>
11723      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11724      * <li>options {Object} The parameter to the request call.</li>
11725      * </ul></li>
11726      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11727      * The callback is passed the following parameters:<ul>
11728      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11729      * <li>options {Object} The parameter to the request call.</li>
11730      * </ul></li>
11731      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11732      * for the callback function. Defaults to the browser window.</li>
11733      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11734      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11735      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11736      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11737      * params for the post data. Any params will be appended to the URL.</li>
11738      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11739      * </ul>
11740      * @return {Number} transactionId
11741      */
11742     request : function(o){
11743         if(this.fireEvent("beforerequest", this, o) !== false){
11744             var p = o.params;
11745
11746             if(typeof p == "function"){
11747                 p = p.call(o.scope||window, o);
11748             }
11749             if(typeof p == "object"){
11750                 p = Roo.urlEncode(o.params);
11751             }
11752             if(this.extraParams){
11753                 var extras = Roo.urlEncode(this.extraParams);
11754                 p = p ? (p + '&' + extras) : extras;
11755             }
11756
11757             var url = o.url || this.url;
11758             if(typeof url == 'function'){
11759                 url = url.call(o.scope||window, o);
11760             }
11761
11762             if(o.form){
11763                 var form = Roo.getDom(o.form);
11764                 url = url || form.action;
11765
11766                 var enctype = form.getAttribute("enctype");
11767                 
11768                 if (o.formData) {
11769                     return this.doFormDataUpload(o, url);
11770                 }
11771                 
11772                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11773                     return this.doFormUpload(o, p, url);
11774                 }
11775                 var f = Roo.lib.Ajax.serializeForm(form);
11776                 p = p ? (p + '&' + f) : f;
11777             }
11778             
11779             if (!o.form && o.formData) {
11780                 o.formData = o.formData === true ? new FormData() : o.formData;
11781                 for (var k in o.params) {
11782                     o.formData.append(k,o.params[k]);
11783                 }
11784                     
11785                 return this.doFormDataUpload(o, url);
11786             }
11787             
11788
11789             var hs = o.headers;
11790             if(this.defaultHeaders){
11791                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11792                 if(!o.headers){
11793                     o.headers = hs;
11794                 }
11795             }
11796
11797             var cb = {
11798                 success: this.handleResponse,
11799                 failure: this.handleFailure,
11800                 scope: this,
11801                 argument: {options: o},
11802                 timeout : o.timeout || this.timeout
11803             };
11804
11805             var method = o.method||this.method||(p ? "POST" : "GET");
11806
11807             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11808                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11809             }
11810
11811             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11812                 if(o.autoAbort){
11813                     this.abort();
11814                 }
11815             }else if(this.autoAbort !== false){
11816                 this.abort();
11817             }
11818
11819             if((method == 'GET' && p) || o.xmlData){
11820                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11821                 p = '';
11822             }
11823             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11824             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11825             Roo.lib.Ajax.useDefaultHeader == true;
11826             return this.transId;
11827         }else{
11828             Roo.callback(o.callback, o.scope, [o, null, null]);
11829             return null;
11830         }
11831     },
11832
11833     /**
11834      * Determine whether this object has a request outstanding.
11835      * @param {Number} transactionId (Optional) defaults to the last transaction
11836      * @return {Boolean} True if there is an outstanding request.
11837      */
11838     isLoading : function(transId){
11839         if(transId){
11840             return Roo.lib.Ajax.isCallInProgress(transId);
11841         }else{
11842             return this.transId ? true : false;
11843         }
11844     },
11845
11846     /**
11847      * Aborts any outstanding request.
11848      * @param {Number} transactionId (Optional) defaults to the last transaction
11849      */
11850     abort : function(transId){
11851         if(transId || this.isLoading()){
11852             Roo.lib.Ajax.abort(transId || this.transId);
11853         }
11854     },
11855
11856     // private
11857     handleResponse : function(response){
11858         this.transId = false;
11859         var options = response.argument.options;
11860         response.argument = options ? options.argument : null;
11861         this.fireEvent("requestcomplete", this, response, options);
11862         Roo.callback(options.success, options.scope, [response, options]);
11863         Roo.callback(options.callback, options.scope, [options, true, response]);
11864     },
11865
11866     // private
11867     handleFailure : function(response, e){
11868         this.transId = false;
11869         var options = response.argument.options;
11870         response.argument = options ? options.argument : null;
11871         this.fireEvent("requestexception", this, response, options, e);
11872         Roo.callback(options.failure, options.scope, [response, options]);
11873         Roo.callback(options.callback, options.scope, [options, false, response]);
11874     },
11875
11876     // private
11877     doFormUpload : function(o, ps, url){
11878         var id = Roo.id();
11879         var frame = document.createElement('iframe');
11880         frame.id = id;
11881         frame.name = id;
11882         frame.className = 'x-hidden';
11883         if(Roo.isIE){
11884             frame.src = Roo.SSL_SECURE_URL;
11885         }
11886         document.body.appendChild(frame);
11887
11888         if(Roo.isIE){
11889            document.frames[id].name = id;
11890         }
11891
11892         var form = Roo.getDom(o.form);
11893         form.target = id;
11894         form.method = 'POST';
11895         form.enctype = form.encoding = 'multipart/form-data';
11896         if(url){
11897             form.action = url;
11898         }
11899
11900         var hiddens, hd;
11901         if(ps){ // add dynamic params
11902             hiddens = [];
11903             ps = Roo.urlDecode(ps, false);
11904             for(var k in ps){
11905                 if(ps.hasOwnProperty(k)){
11906                     hd = document.createElement('input');
11907                     hd.type = 'hidden';
11908                     hd.name = k;
11909                     hd.value = ps[k];
11910                     form.appendChild(hd);
11911                     hiddens.push(hd);
11912                 }
11913             }
11914         }
11915
11916         function cb(){
11917             var r = {  // bogus response object
11918                 responseText : '',
11919                 responseXML : null
11920             };
11921
11922             r.argument = o ? o.argument : null;
11923
11924             try { //
11925                 var doc;
11926                 if(Roo.isIE){
11927                     doc = frame.contentWindow.document;
11928                 }else {
11929                     doc = (frame.contentDocument || window.frames[id].document);
11930                 }
11931                 if(doc && doc.body){
11932                     r.responseText = doc.body.innerHTML;
11933                 }
11934                 if(doc && doc.XMLDocument){
11935                     r.responseXML = doc.XMLDocument;
11936                 }else {
11937                     r.responseXML = doc;
11938                 }
11939             }
11940             catch(e) {
11941                 // ignore
11942             }
11943
11944             Roo.EventManager.removeListener(frame, 'load', cb, this);
11945
11946             this.fireEvent("requestcomplete", this, r, o);
11947             Roo.callback(o.success, o.scope, [r, o]);
11948             Roo.callback(o.callback, o.scope, [o, true, r]);
11949
11950             setTimeout(function(){document.body.removeChild(frame);}, 100);
11951         }
11952
11953         Roo.EventManager.on(frame, 'load', cb, this);
11954         form.submit();
11955
11956         if(hiddens){ // remove dynamic params
11957             for(var i = 0, len = hiddens.length; i < len; i++){
11958                 form.removeChild(hiddens[i]);
11959             }
11960         }
11961     },
11962     // this is a 'formdata version???'
11963     
11964     
11965     doFormDataUpload : function(o,  url)
11966     {
11967         var formData;
11968         if (o.form) {
11969             var form =  Roo.getDom(o.form);
11970             form.enctype = form.encoding = 'multipart/form-data';
11971             formData = o.formData === true ? new FormData(form) : o.formData;
11972         } else {
11973             formData = o.formData === true ? new FormData() : o.formData;
11974         }
11975         
11976       
11977         var cb = {
11978             success: this.handleResponse,
11979             failure: this.handleFailure,
11980             scope: this,
11981             argument: {options: o},
11982             timeout : o.timeout || this.timeout
11983         };
11984  
11985         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11986             if(o.autoAbort){
11987                 this.abort();
11988             }
11989         }else if(this.autoAbort !== false){
11990             this.abort();
11991         }
11992
11993         //Roo.lib.Ajax.defaultPostHeader = null;
11994         Roo.lib.Ajax.useDefaultHeader = false;
11995         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11996         Roo.lib.Ajax.useDefaultHeader = true;
11997  
11998          
11999     }
12000     
12001 });
12002 /*
12003  * Based on:
12004  * Ext JS Library 1.1.1
12005  * Copyright(c) 2006-2007, Ext JS, LLC.
12006  *
12007  * Originally Released Under LGPL - original licence link has changed is not relivant.
12008  *
12009  * Fork - LGPL
12010  * <script type="text/javascript">
12011  */
12012  
12013 /**
12014  * Global Ajax request class.
12015  * 
12016  * @class Roo.Ajax
12017  * @extends Roo.data.Connection
12018  * @static
12019  * 
12020  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12021  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12022  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12023  * @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)
12024  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12025  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12026  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12027  */
12028 Roo.Ajax = new Roo.data.Connection({
12029     // fix up the docs
12030     /**
12031      * @scope Roo.Ajax
12032      * @type {Boolear} 
12033      */
12034     autoAbort : false,
12035
12036     /**
12037      * Serialize the passed form into a url encoded string
12038      * @scope Roo.Ajax
12039      * @param {String/HTMLElement} form
12040      * @return {String}
12041      */
12042     serializeForm : function(form){
12043         return Roo.lib.Ajax.serializeForm(form);
12044     }
12045 });/*
12046  * Based on:
12047  * Ext JS Library 1.1.1
12048  * Copyright(c) 2006-2007, Ext JS, LLC.
12049  *
12050  * Originally Released Under LGPL - original licence link has changed is not relivant.
12051  *
12052  * Fork - LGPL
12053  * <script type="text/javascript">
12054  */
12055
12056  
12057 /**
12058  * @class Roo.UpdateManager
12059  * @extends Roo.util.Observable
12060  * Provides AJAX-style update for Element object.<br><br>
12061  * Usage:<br>
12062  * <pre><code>
12063  * // Get it from a Roo.Element object
12064  * var el = Roo.get("foo");
12065  * var mgr = el.getUpdateManager();
12066  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12067  * ...
12068  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12069  * <br>
12070  * // or directly (returns the same UpdateManager instance)
12071  * var mgr = new Roo.UpdateManager("myElementId");
12072  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12073  * mgr.on("update", myFcnNeedsToKnow);
12074  * <br>
12075    // short handed call directly from the element object
12076    Roo.get("foo").load({
12077         url: "bar.php",
12078         scripts:true,
12079         params: "for=bar",
12080         text: "Loading Foo..."
12081    });
12082  * </code></pre>
12083  * @constructor
12084  * Create new UpdateManager directly.
12085  * @param {String/HTMLElement/Roo.Element} el The element to update
12086  * @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).
12087  */
12088 Roo.UpdateManager = function(el, forceNew){
12089     el = Roo.get(el);
12090     if(!forceNew && el.updateManager){
12091         return el.updateManager;
12092     }
12093     /**
12094      * The Element object
12095      * @type Roo.Element
12096      */
12097     this.el = el;
12098     /**
12099      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12100      * @type String
12101      */
12102     this.defaultUrl = null;
12103
12104     this.addEvents({
12105         /**
12106          * @event beforeupdate
12107          * Fired before an update is made, return false from your handler and the update is cancelled.
12108          * @param {Roo.Element} el
12109          * @param {String/Object/Function} url
12110          * @param {String/Object} params
12111          */
12112         "beforeupdate": true,
12113         /**
12114          * @event update
12115          * Fired after successful update is made.
12116          * @param {Roo.Element} el
12117          * @param {Object} oResponseObject The response Object
12118          */
12119         "update": true,
12120         /**
12121          * @event failure
12122          * Fired on update failure.
12123          * @param {Roo.Element} el
12124          * @param {Object} oResponseObject The response Object
12125          */
12126         "failure": true
12127     });
12128     var d = Roo.UpdateManager.defaults;
12129     /**
12130      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12131      * @type String
12132      */
12133     this.sslBlankUrl = d.sslBlankUrl;
12134     /**
12135      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12136      * @type Boolean
12137      */
12138     this.disableCaching = d.disableCaching;
12139     /**
12140      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12141      * @type String
12142      */
12143     this.indicatorText = d.indicatorText;
12144     /**
12145      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12146      * @type String
12147      */
12148     this.showLoadIndicator = d.showLoadIndicator;
12149     /**
12150      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12151      * @type Number
12152      */
12153     this.timeout = d.timeout;
12154
12155     /**
12156      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12157      * @type Boolean
12158      */
12159     this.loadScripts = d.loadScripts;
12160
12161     /**
12162      * Transaction object of current executing transaction
12163      */
12164     this.transaction = null;
12165
12166     /**
12167      * @private
12168      */
12169     this.autoRefreshProcId = null;
12170     /**
12171      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12172      * @type Function
12173      */
12174     this.refreshDelegate = this.refresh.createDelegate(this);
12175     /**
12176      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12177      * @type Function
12178      */
12179     this.updateDelegate = this.update.createDelegate(this);
12180     /**
12181      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12182      * @type Function
12183      */
12184     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12185     /**
12186      * @private
12187      */
12188     this.successDelegate = this.processSuccess.createDelegate(this);
12189     /**
12190      * @private
12191      */
12192     this.failureDelegate = this.processFailure.createDelegate(this);
12193
12194     if(!this.renderer){
12195      /**
12196       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12197       */
12198     this.renderer = new Roo.UpdateManager.BasicRenderer();
12199     }
12200     
12201     Roo.UpdateManager.superclass.constructor.call(this);
12202 };
12203
12204 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12205     /**
12206      * Get the Element this UpdateManager is bound to
12207      * @return {Roo.Element} The element
12208      */
12209     getEl : function(){
12210         return this.el;
12211     },
12212     /**
12213      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12214      * @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:
12215 <pre><code>
12216 um.update({<br/>
12217     url: "your-url.php",<br/>
12218     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12219     callback: yourFunction,<br/>
12220     scope: yourObject, //(optional scope)  <br/>
12221     discardUrl: false, <br/>
12222     nocache: false,<br/>
12223     text: "Loading...",<br/>
12224     timeout: 30,<br/>
12225     scripts: false<br/>
12226 });
12227 </code></pre>
12228      * The only required property is url. The optional properties nocache, text and scripts
12229      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12230      * @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}
12231      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12232      * @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.
12233      */
12234     update : function(url, params, callback, discardUrl){
12235         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12236             var method = this.method,
12237                 cfg;
12238             if(typeof url == "object"){ // must be config object
12239                 cfg = url;
12240                 url = cfg.url;
12241                 params = params || cfg.params;
12242                 callback = callback || cfg.callback;
12243                 discardUrl = discardUrl || cfg.discardUrl;
12244                 if(callback && cfg.scope){
12245                     callback = callback.createDelegate(cfg.scope);
12246                 }
12247                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12248                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12249                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12250                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12251                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12252             }
12253             this.showLoading();
12254             if(!discardUrl){
12255                 this.defaultUrl = url;
12256             }
12257             if(typeof url == "function"){
12258                 url = url.call(this);
12259             }
12260
12261             method = method || (params ? "POST" : "GET");
12262             if(method == "GET"){
12263                 url = this.prepareUrl(url);
12264             }
12265
12266             var o = Roo.apply(cfg ||{}, {
12267                 url : url,
12268                 params: params,
12269                 success: this.successDelegate,
12270                 failure: this.failureDelegate,
12271                 callback: undefined,
12272                 timeout: (this.timeout*1000),
12273                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12274             });
12275             Roo.log("updated manager called with timeout of " + o.timeout);
12276             this.transaction = Roo.Ajax.request(o);
12277         }
12278     },
12279
12280     /**
12281      * 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.
12282      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12283      * @param {String/HTMLElement} form The form Id or form element
12284      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12285      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12286      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12287      */
12288     formUpdate : function(form, url, reset, callback){
12289         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12290             if(typeof url == "function"){
12291                 url = url.call(this);
12292             }
12293             form = Roo.getDom(form);
12294             this.transaction = Roo.Ajax.request({
12295                 form: form,
12296                 url:url,
12297                 success: this.successDelegate,
12298                 failure: this.failureDelegate,
12299                 timeout: (this.timeout*1000),
12300                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12301             });
12302             this.showLoading.defer(1, this);
12303         }
12304     },
12305
12306     /**
12307      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12308      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12309      */
12310     refresh : function(callback){
12311         if(this.defaultUrl == null){
12312             return;
12313         }
12314         this.update(this.defaultUrl, null, callback, true);
12315     },
12316
12317     /**
12318      * Set this element to auto refresh.
12319      * @param {Number} interval How often to update (in seconds).
12320      * @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)
12321      * @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}
12322      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12323      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12324      */
12325     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12326         if(refreshNow){
12327             this.update(url || this.defaultUrl, params, callback, true);
12328         }
12329         if(this.autoRefreshProcId){
12330             clearInterval(this.autoRefreshProcId);
12331         }
12332         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12333     },
12334
12335     /**
12336      * Stop auto refresh on this element.
12337      */
12338      stopAutoRefresh : function(){
12339         if(this.autoRefreshProcId){
12340             clearInterval(this.autoRefreshProcId);
12341             delete this.autoRefreshProcId;
12342         }
12343     },
12344
12345     isAutoRefreshing : function(){
12346        return this.autoRefreshProcId ? true : false;
12347     },
12348     /**
12349      * Called to update the element to "Loading" state. Override to perform custom action.
12350      */
12351     showLoading : function(){
12352         if(this.showLoadIndicator){
12353             this.el.update(this.indicatorText);
12354         }
12355     },
12356
12357     /**
12358      * Adds unique parameter to query string if disableCaching = true
12359      * @private
12360      */
12361     prepareUrl : function(url){
12362         if(this.disableCaching){
12363             var append = "_dc=" + (new Date().getTime());
12364             if(url.indexOf("?") !== -1){
12365                 url += "&" + append;
12366             }else{
12367                 url += "?" + append;
12368             }
12369         }
12370         return url;
12371     },
12372
12373     /**
12374      * @private
12375      */
12376     processSuccess : function(response){
12377         this.transaction = null;
12378         if(response.argument.form && response.argument.reset){
12379             try{ // put in try/catch since some older FF releases had problems with this
12380                 response.argument.form.reset();
12381             }catch(e){}
12382         }
12383         if(this.loadScripts){
12384             this.renderer.render(this.el, response, this,
12385                 this.updateComplete.createDelegate(this, [response]));
12386         }else{
12387             this.renderer.render(this.el, response, this);
12388             this.updateComplete(response);
12389         }
12390     },
12391
12392     updateComplete : function(response){
12393         this.fireEvent("update", this.el, response);
12394         if(typeof response.argument.callback == "function"){
12395             response.argument.callback(this.el, true, response);
12396         }
12397     },
12398
12399     /**
12400      * @private
12401      */
12402     processFailure : function(response){
12403         this.transaction = null;
12404         this.fireEvent("failure", this.el, response);
12405         if(typeof response.argument.callback == "function"){
12406             response.argument.callback(this.el, false, response);
12407         }
12408     },
12409
12410     /**
12411      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12412      * @param {Object} renderer The object implementing the render() method
12413      */
12414     setRenderer : function(renderer){
12415         this.renderer = renderer;
12416     },
12417
12418     getRenderer : function(){
12419        return this.renderer;
12420     },
12421
12422     /**
12423      * Set the defaultUrl used for updates
12424      * @param {String/Function} defaultUrl The url or a function to call to get the url
12425      */
12426     setDefaultUrl : function(defaultUrl){
12427         this.defaultUrl = defaultUrl;
12428     },
12429
12430     /**
12431      * Aborts the executing transaction
12432      */
12433     abort : function(){
12434         if(this.transaction){
12435             Roo.Ajax.abort(this.transaction);
12436         }
12437     },
12438
12439     /**
12440      * Returns true if an update is in progress
12441      * @return {Boolean}
12442      */
12443     isUpdating : function(){
12444         if(this.transaction){
12445             return Roo.Ajax.isLoading(this.transaction);
12446         }
12447         return false;
12448     }
12449 });
12450
12451 /**
12452  * @class Roo.UpdateManager.defaults
12453  * @static (not really - but it helps the doc tool)
12454  * The defaults collection enables customizing the default properties of UpdateManager
12455  */
12456    Roo.UpdateManager.defaults = {
12457        /**
12458          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12459          * @type Number
12460          */
12461          timeout : 30,
12462
12463          /**
12464          * True to process scripts by default (Defaults to false).
12465          * @type Boolean
12466          */
12467         loadScripts : false,
12468
12469         /**
12470         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12471         * @type String
12472         */
12473         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12474         /**
12475          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12476          * @type Boolean
12477          */
12478         disableCaching : false,
12479         /**
12480          * Whether to show indicatorText when loading (Defaults to true).
12481          * @type Boolean
12482          */
12483         showLoadIndicator : true,
12484         /**
12485          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12486          * @type String
12487          */
12488         indicatorText : '<div class="loading-indicator">Loading...</div>'
12489    };
12490
12491 /**
12492  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12493  *Usage:
12494  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12495  * @param {String/HTMLElement/Roo.Element} el The element to update
12496  * @param {String} url The url
12497  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12498  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12499  * @static
12500  * @deprecated
12501  * @member Roo.UpdateManager
12502  */
12503 Roo.UpdateManager.updateElement = function(el, url, params, options){
12504     var um = Roo.get(el, true).getUpdateManager();
12505     Roo.apply(um, options);
12506     um.update(url, params, options ? options.callback : null);
12507 };
12508 // alias for backwards compat
12509 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12510 /**
12511  * @class Roo.UpdateManager.BasicRenderer
12512  * Default Content renderer. Updates the elements innerHTML with the responseText.
12513  */
12514 Roo.UpdateManager.BasicRenderer = function(){};
12515
12516 Roo.UpdateManager.BasicRenderer.prototype = {
12517     /**
12518      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12519      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12520      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12521      * @param {Roo.Element} el The element being rendered
12522      * @param {Object} response The YUI Connect response object
12523      * @param {UpdateManager} updateManager The calling update manager
12524      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12525      */
12526      render : function(el, response, updateManager, callback){
12527         el.update(response.responseText, updateManager.loadScripts, callback);
12528     }
12529 };
12530 /*
12531  * Based on:
12532  * Roo JS
12533  * (c)) Alan Knowles
12534  * Licence : LGPL
12535  */
12536
12537
12538 /**
12539  * @class Roo.DomTemplate
12540  * @extends Roo.Template
12541  * An effort at a dom based template engine..
12542  *
12543  * Similar to XTemplate, except it uses dom parsing to create the template..
12544  *
12545  * Supported features:
12546  *
12547  *  Tags:
12548
12549 <pre><code>
12550       {a_variable} - output encoded.
12551       {a_variable.format:("Y-m-d")} - call a method on the variable
12552       {a_variable:raw} - unencoded output
12553       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12554       {a_variable:this.method_on_template(...)} - call a method on the template object.
12555  
12556 </code></pre>
12557  *  The tpl tag:
12558 <pre><code>
12559         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12560         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12561         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12562         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12563   
12564 </code></pre>
12565  *      
12566  */
12567 Roo.DomTemplate = function()
12568 {
12569      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12570      if (this.html) {
12571         this.compile();
12572      }
12573 };
12574
12575
12576 Roo.extend(Roo.DomTemplate, Roo.Template, {
12577     /**
12578      * id counter for sub templates.
12579      */
12580     id : 0,
12581     /**
12582      * flag to indicate if dom parser is inside a pre,
12583      * it will strip whitespace if not.
12584      */
12585     inPre : false,
12586     
12587     /**
12588      * The various sub templates
12589      */
12590     tpls : false,
12591     
12592     
12593     
12594     /**
12595      *
12596      * basic tag replacing syntax
12597      * WORD:WORD()
12598      *
12599      * // you can fake an object call by doing this
12600      *  x.t:(test,tesT) 
12601      * 
12602      */
12603     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12604     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12605     
12606     iterChild : function (node, method) {
12607         
12608         var oldPre = this.inPre;
12609         if (node.tagName == 'PRE') {
12610             this.inPre = true;
12611         }
12612         for( var i = 0; i < node.childNodes.length; i++) {
12613             method.call(this, node.childNodes[i]);
12614         }
12615         this.inPre = oldPre;
12616     },
12617     
12618     
12619     
12620     /**
12621      * compile the template
12622      *
12623      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12624      *
12625      */
12626     compile: function()
12627     {
12628         var s = this.html;
12629         
12630         // covert the html into DOM...
12631         var doc = false;
12632         var div =false;
12633         try {
12634             doc = document.implementation.createHTMLDocument("");
12635             doc.documentElement.innerHTML =   this.html  ;
12636             div = doc.documentElement;
12637         } catch (e) {
12638             // old IE... - nasty -- it causes all sorts of issues.. with
12639             // images getting pulled from server..
12640             div = document.createElement('div');
12641             div.innerHTML = this.html;
12642         }
12643         //doc.documentElement.innerHTML = htmlBody
12644          
12645         
12646         
12647         this.tpls = [];
12648         var _t = this;
12649         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12650         
12651         var tpls = this.tpls;
12652         
12653         // create a top level template from the snippet..
12654         
12655         //Roo.log(div.innerHTML);
12656         
12657         var tpl = {
12658             uid : 'master',
12659             id : this.id++,
12660             attr : false,
12661             value : false,
12662             body : div.innerHTML,
12663             
12664             forCall : false,
12665             execCall : false,
12666             dom : div,
12667             isTop : true
12668             
12669         };
12670         tpls.unshift(tpl);
12671         
12672         
12673         // compile them...
12674         this.tpls = [];
12675         Roo.each(tpls, function(tp){
12676             this.compileTpl(tp);
12677             this.tpls[tp.id] = tp;
12678         }, this);
12679         
12680         this.master = tpls[0];
12681         return this;
12682         
12683         
12684     },
12685     
12686     compileNode : function(node, istop) {
12687         // test for
12688         //Roo.log(node);
12689         
12690         
12691         // skip anything not a tag..
12692         if (node.nodeType != 1) {
12693             if (node.nodeType == 3 && !this.inPre) {
12694                 // reduce white space..
12695                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12696                 
12697             }
12698             return;
12699         }
12700         
12701         var tpl = {
12702             uid : false,
12703             id : false,
12704             attr : false,
12705             value : false,
12706             body : '',
12707             
12708             forCall : false,
12709             execCall : false,
12710             dom : false,
12711             isTop : istop
12712             
12713             
12714         };
12715         
12716         
12717         switch(true) {
12718             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12719             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12720             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12721             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12722             // no default..
12723         }
12724         
12725         
12726         if (!tpl.attr) {
12727             // just itterate children..
12728             this.iterChild(node,this.compileNode);
12729             return;
12730         }
12731         tpl.uid = this.id++;
12732         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12733         node.removeAttribute('roo-'+ tpl.attr);
12734         if (tpl.attr != 'name') {
12735             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12736             node.parentNode.replaceChild(placeholder,  node);
12737         } else {
12738             
12739             var placeholder =  document.createElement('span');
12740             placeholder.className = 'roo-tpl-' + tpl.value;
12741             node.parentNode.replaceChild(placeholder,  node);
12742         }
12743         
12744         // parent now sees '{domtplXXXX}
12745         this.iterChild(node,this.compileNode);
12746         
12747         // we should now have node body...
12748         var div = document.createElement('div');
12749         div.appendChild(node);
12750         tpl.dom = node;
12751         // this has the unfortunate side effect of converting tagged attributes
12752         // eg. href="{...}" into %7C...%7D
12753         // this has been fixed by searching for those combo's although it's a bit hacky..
12754         
12755         
12756         tpl.body = div.innerHTML;
12757         
12758         
12759          
12760         tpl.id = tpl.uid;
12761         switch(tpl.attr) {
12762             case 'for' :
12763                 switch (tpl.value) {
12764                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12765                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12766                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12767                 }
12768                 break;
12769             
12770             case 'exec':
12771                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12772                 break;
12773             
12774             case 'if':     
12775                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12776                 break;
12777             
12778             case 'name':
12779                 tpl.id  = tpl.value; // replace non characters???
12780                 break;
12781             
12782         }
12783         
12784         
12785         this.tpls.push(tpl);
12786         
12787         
12788         
12789     },
12790     
12791     
12792     
12793     
12794     /**
12795      * Compile a segment of the template into a 'sub-template'
12796      *
12797      * 
12798      * 
12799      *
12800      */
12801     compileTpl : function(tpl)
12802     {
12803         var fm = Roo.util.Format;
12804         var useF = this.disableFormats !== true;
12805         
12806         var sep = Roo.isGecko ? "+\n" : ",\n";
12807         
12808         var undef = function(str) {
12809             Roo.debug && Roo.log("Property not found :"  + str);
12810             return '';
12811         };
12812           
12813         //Roo.log(tpl.body);
12814         
12815         
12816         
12817         var fn = function(m, lbrace, name, format, args)
12818         {
12819             //Roo.log("ARGS");
12820             //Roo.log(arguments);
12821             args = args ? args.replace(/\\'/g,"'") : args;
12822             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12823             if (typeof(format) == 'undefined') {
12824                 format =  'htmlEncode'; 
12825             }
12826             if (format == 'raw' ) {
12827                 format = false;
12828             }
12829             
12830             if(name.substr(0, 6) == 'domtpl'){
12831                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12832             }
12833             
12834             // build an array of options to determine if value is undefined..
12835             
12836             // basically get 'xxxx.yyyy' then do
12837             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12838             //    (function () { Roo.log("Property not found"); return ''; })() :
12839             //    ......
12840             
12841             var udef_ar = [];
12842             var lookfor = '';
12843             Roo.each(name.split('.'), function(st) {
12844                 lookfor += (lookfor.length ? '.': '') + st;
12845                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12846             });
12847             
12848             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12849             
12850             
12851             if(format && useF){
12852                 
12853                 args = args ? ',' + args : "";
12854                  
12855                 if(format.substr(0, 5) != "this."){
12856                     format = "fm." + format + '(';
12857                 }else{
12858                     format = 'this.call("'+ format.substr(5) + '", ';
12859                     args = ", values";
12860                 }
12861                 
12862                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12863             }
12864              
12865             if (args && args.length) {
12866                 // called with xxyx.yuu:(test,test)
12867                 // change to ()
12868                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12869             }
12870             // raw.. - :raw modifier..
12871             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12872             
12873         };
12874         var body;
12875         // branched to use + in gecko and [].join() in others
12876         if(Roo.isGecko){
12877             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12878                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12879                     "';};};";
12880         }else{
12881             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12882             body.push(tpl.body.replace(/(\r\n|\n)/g,
12883                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12884             body.push("'].join('');};};");
12885             body = body.join('');
12886         }
12887         
12888         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12889        
12890         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12891         eval(body);
12892         
12893         return this;
12894     },
12895      
12896     /**
12897      * same as applyTemplate, except it's done to one of the subTemplates
12898      * when using named templates, you can do:
12899      *
12900      * var str = pl.applySubTemplate('your-name', values);
12901      *
12902      * 
12903      * @param {Number} id of the template
12904      * @param {Object} values to apply to template
12905      * @param {Object} parent (normaly the instance of this object)
12906      */
12907     applySubTemplate : function(id, values, parent)
12908     {
12909         
12910         
12911         var t = this.tpls[id];
12912         
12913         
12914         try { 
12915             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12916                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12917                 return '';
12918             }
12919         } catch(e) {
12920             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12921             Roo.log(values);
12922           
12923             return '';
12924         }
12925         try { 
12926             
12927             if(t.execCall && t.execCall.call(this, values, parent)){
12928                 return '';
12929             }
12930         } catch(e) {
12931             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12932             Roo.log(values);
12933             return '';
12934         }
12935         
12936         try {
12937             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12938             parent = t.target ? values : parent;
12939             if(t.forCall && vs instanceof Array){
12940                 var buf = [];
12941                 for(var i = 0, len = vs.length; i < len; i++){
12942                     try {
12943                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12944                     } catch (e) {
12945                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12946                         Roo.log(e.body);
12947                         //Roo.log(t.compiled);
12948                         Roo.log(vs[i]);
12949                     }   
12950                 }
12951                 return buf.join('');
12952             }
12953         } catch (e) {
12954             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12955             Roo.log(values);
12956             return '';
12957         }
12958         try {
12959             return t.compiled.call(this, vs, parent);
12960         } catch (e) {
12961             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12962             Roo.log(e.body);
12963             //Roo.log(t.compiled);
12964             Roo.log(values);
12965             return '';
12966         }
12967     },
12968
12969    
12970
12971     applyTemplate : function(values){
12972         return this.master.compiled.call(this, values, {});
12973         //var s = this.subs;
12974     },
12975
12976     apply : function(){
12977         return this.applyTemplate.apply(this, arguments);
12978     }
12979
12980  });
12981
12982 Roo.DomTemplate.from = function(el){
12983     el = Roo.getDom(el);
12984     return new Roo.Domtemplate(el.value || el.innerHTML);
12985 };/*
12986  * Based on:
12987  * Ext JS Library 1.1.1
12988  * Copyright(c) 2006-2007, Ext JS, LLC.
12989  *
12990  * Originally Released Under LGPL - original licence link has changed is not relivant.
12991  *
12992  * Fork - LGPL
12993  * <script type="text/javascript">
12994  */
12995
12996 /**
12997  * @class Roo.util.DelayedTask
12998  * Provides a convenient method of performing setTimeout where a new
12999  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13000  * You can use this class to buffer
13001  * the keypress events for a certain number of milliseconds, and perform only if they stop
13002  * for that amount of time.
13003  * @constructor The parameters to this constructor serve as defaults and are not required.
13004  * @param {Function} fn (optional) The default function to timeout
13005  * @param {Object} scope (optional) The default scope of that timeout
13006  * @param {Array} args (optional) The default Array of arguments
13007  */
13008 Roo.util.DelayedTask = function(fn, scope, args){
13009     var id = null, d, t;
13010
13011     var call = function(){
13012         var now = new Date().getTime();
13013         if(now - t >= d){
13014             clearInterval(id);
13015             id = null;
13016             fn.apply(scope, args || []);
13017         }
13018     };
13019     /**
13020      * Cancels any pending timeout and queues a new one
13021      * @param {Number} delay The milliseconds to delay
13022      * @param {Function} newFn (optional) Overrides function passed to constructor
13023      * @param {Object} newScope (optional) Overrides scope passed to constructor
13024      * @param {Array} newArgs (optional) Overrides args passed to constructor
13025      */
13026     this.delay = function(delay, newFn, newScope, newArgs){
13027         if(id && delay != d){
13028             this.cancel();
13029         }
13030         d = delay;
13031         t = new Date().getTime();
13032         fn = newFn || fn;
13033         scope = newScope || scope;
13034         args = newArgs || args;
13035         if(!id){
13036             id = setInterval(call, d);
13037         }
13038     };
13039
13040     /**
13041      * Cancel the last queued timeout
13042      */
13043     this.cancel = function(){
13044         if(id){
13045             clearInterval(id);
13046             id = null;
13047         }
13048     };
13049 };/*
13050  * Based on:
13051  * Ext JS Library 1.1.1
13052  * Copyright(c) 2006-2007, Ext JS, LLC.
13053  *
13054  * Originally Released Under LGPL - original licence link has changed is not relivant.
13055  *
13056  * Fork - LGPL
13057  * <script type="text/javascript">
13058  */
13059  
13060  
13061 Roo.util.TaskRunner = function(interval){
13062     interval = interval || 10;
13063     var tasks = [], removeQueue = [];
13064     var id = 0;
13065     var running = false;
13066
13067     var stopThread = function(){
13068         running = false;
13069         clearInterval(id);
13070         id = 0;
13071     };
13072
13073     var startThread = function(){
13074         if(!running){
13075             running = true;
13076             id = setInterval(runTasks, interval);
13077         }
13078     };
13079
13080     var removeTask = function(task){
13081         removeQueue.push(task);
13082         if(task.onStop){
13083             task.onStop();
13084         }
13085     };
13086
13087     var runTasks = function(){
13088         if(removeQueue.length > 0){
13089             for(var i = 0, len = removeQueue.length; i < len; i++){
13090                 tasks.remove(removeQueue[i]);
13091             }
13092             removeQueue = [];
13093             if(tasks.length < 1){
13094                 stopThread();
13095                 return;
13096             }
13097         }
13098         var now = new Date().getTime();
13099         for(var i = 0, len = tasks.length; i < len; ++i){
13100             var t = tasks[i];
13101             var itime = now - t.taskRunTime;
13102             if(t.interval <= itime){
13103                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13104                 t.taskRunTime = now;
13105                 if(rt === false || t.taskRunCount === t.repeat){
13106                     removeTask(t);
13107                     return;
13108                 }
13109             }
13110             if(t.duration && t.duration <= (now - t.taskStartTime)){
13111                 removeTask(t);
13112             }
13113         }
13114     };
13115
13116     /**
13117      * Queues a new task.
13118      * @param {Object} task
13119      */
13120     this.start = function(task){
13121         tasks.push(task);
13122         task.taskStartTime = new Date().getTime();
13123         task.taskRunTime = 0;
13124         task.taskRunCount = 0;
13125         startThread();
13126         return task;
13127     };
13128
13129     this.stop = function(task){
13130         removeTask(task);
13131         return task;
13132     };
13133
13134     this.stopAll = function(){
13135         stopThread();
13136         for(var i = 0, len = tasks.length; i < len; i++){
13137             if(tasks[i].onStop){
13138                 tasks[i].onStop();
13139             }
13140         }
13141         tasks = [];
13142         removeQueue = [];
13143     };
13144 };
13145
13146 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157  
13158 /**
13159  * @class Roo.util.MixedCollection
13160  * @extends Roo.util.Observable
13161  * A Collection class that maintains both numeric indexes and keys and exposes events.
13162  * @constructor
13163  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13164  * collection (defaults to false)
13165  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13166  * and return the key value for that item.  This is used when available to look up the key on items that
13167  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13168  * equivalent to providing an implementation for the {@link #getKey} method.
13169  */
13170 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13171     this.items = [];
13172     this.map = {};
13173     this.keys = [];
13174     this.length = 0;
13175     this.addEvents({
13176         /**
13177          * @event clear
13178          * Fires when the collection is cleared.
13179          */
13180         "clear" : true,
13181         /**
13182          * @event add
13183          * Fires when an item is added to the collection.
13184          * @param {Number} index The index at which the item was added.
13185          * @param {Object} o The item added.
13186          * @param {String} key The key associated with the added item.
13187          */
13188         "add" : true,
13189         /**
13190          * @event replace
13191          * Fires when an item is replaced in the collection.
13192          * @param {String} key he key associated with the new added.
13193          * @param {Object} old The item being replaced.
13194          * @param {Object} new The new item.
13195          */
13196         "replace" : true,
13197         /**
13198          * @event remove
13199          * Fires when an item is removed from the collection.
13200          * @param {Object} o The item being removed.
13201          * @param {String} key (optional) The key associated with the removed item.
13202          */
13203         "remove" : true,
13204         "sort" : true
13205     });
13206     this.allowFunctions = allowFunctions === true;
13207     if(keyFn){
13208         this.getKey = keyFn;
13209     }
13210     Roo.util.MixedCollection.superclass.constructor.call(this);
13211 };
13212
13213 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13214     allowFunctions : false,
13215     
13216 /**
13217  * Adds an item to the collection.
13218  * @param {String} key The key to associate with the item
13219  * @param {Object} o The item to add.
13220  * @return {Object} The item added.
13221  */
13222     add : function(key, o){
13223         if(arguments.length == 1){
13224             o = arguments[0];
13225             key = this.getKey(o);
13226         }
13227         if(typeof key == "undefined" || key === null){
13228             this.length++;
13229             this.items.push(o);
13230             this.keys.push(null);
13231         }else{
13232             var old = this.map[key];
13233             if(old){
13234                 return this.replace(key, o);
13235             }
13236             this.length++;
13237             this.items.push(o);
13238             this.map[key] = o;
13239             this.keys.push(key);
13240         }
13241         this.fireEvent("add", this.length-1, o, key);
13242         return o;
13243     },
13244        
13245 /**
13246   * MixedCollection has a generic way to fetch keys if you implement getKey.
13247 <pre><code>
13248 // normal way
13249 var mc = new Roo.util.MixedCollection();
13250 mc.add(someEl.dom.id, someEl);
13251 mc.add(otherEl.dom.id, otherEl);
13252 //and so on
13253
13254 // using getKey
13255 var mc = new Roo.util.MixedCollection();
13256 mc.getKey = function(el){
13257    return el.dom.id;
13258 };
13259 mc.add(someEl);
13260 mc.add(otherEl);
13261
13262 // or via the constructor
13263 var mc = new Roo.util.MixedCollection(false, function(el){
13264    return el.dom.id;
13265 });
13266 mc.add(someEl);
13267 mc.add(otherEl);
13268 </code></pre>
13269  * @param o {Object} The item for which to find the key.
13270  * @return {Object} The key for the passed item.
13271  */
13272     getKey : function(o){
13273          return o.id; 
13274     },
13275    
13276 /**
13277  * Replaces an item in the collection.
13278  * @param {String} key The key associated with the item to replace, or the item to replace.
13279  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13280  * @return {Object}  The new item.
13281  */
13282     replace : function(key, o){
13283         if(arguments.length == 1){
13284             o = arguments[0];
13285             key = this.getKey(o);
13286         }
13287         var old = this.item(key);
13288         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13289              return this.add(key, o);
13290         }
13291         var index = this.indexOfKey(key);
13292         this.items[index] = o;
13293         this.map[key] = o;
13294         this.fireEvent("replace", key, old, o);
13295         return o;
13296     },
13297    
13298 /**
13299  * Adds all elements of an Array or an Object to the collection.
13300  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13301  * an Array of values, each of which are added to the collection.
13302  */
13303     addAll : function(objs){
13304         if(arguments.length > 1 || objs instanceof Array){
13305             var args = arguments.length > 1 ? arguments : objs;
13306             for(var i = 0, len = args.length; i < len; i++){
13307                 this.add(args[i]);
13308             }
13309         }else{
13310             for(var key in objs){
13311                 if(this.allowFunctions || typeof objs[key] != "function"){
13312                     this.add(key, objs[key]);
13313                 }
13314             }
13315         }
13316     },
13317    
13318 /**
13319  * Executes the specified function once for every item in the collection, passing each
13320  * item as the first and only parameter. returning false from the function will stop the iteration.
13321  * @param {Function} fn The function to execute for each item.
13322  * @param {Object} scope (optional) The scope in which to execute the function.
13323  */
13324     each : function(fn, scope){
13325         var items = [].concat(this.items); // each safe for removal
13326         for(var i = 0, len = items.length; i < len; i++){
13327             if(fn.call(scope || items[i], items[i], i, len) === false){
13328                 break;
13329             }
13330         }
13331     },
13332    
13333 /**
13334  * Executes the specified function once for every key in the collection, passing each
13335  * key, and its associated item as the first two parameters.
13336  * @param {Function} fn The function to execute for each item.
13337  * @param {Object} scope (optional) The scope in which to execute the function.
13338  */
13339     eachKey : function(fn, scope){
13340         for(var i = 0, len = this.keys.length; i < len; i++){
13341             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13342         }
13343     },
13344    
13345 /**
13346  * Returns the first item in the collection which elicits a true return value from the
13347  * passed selection function.
13348  * @param {Function} fn The selection function to execute for each item.
13349  * @param {Object} scope (optional) The scope in which to execute the function.
13350  * @return {Object} The first item in the collection which returned true from the selection function.
13351  */
13352     find : function(fn, scope){
13353         for(var i = 0, len = this.items.length; i < len; i++){
13354             if(fn.call(scope || window, this.items[i], this.keys[i])){
13355                 return this.items[i];
13356             }
13357         }
13358         return null;
13359     },
13360    
13361 /**
13362  * Inserts an item at the specified index in the collection.
13363  * @param {Number} index The index to insert the item at.
13364  * @param {String} key The key to associate with the new item, or the item itself.
13365  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13366  * @return {Object} The item inserted.
13367  */
13368     insert : function(index, key, o){
13369         if(arguments.length == 2){
13370             o = arguments[1];
13371             key = this.getKey(o);
13372         }
13373         if(index >= this.length){
13374             return this.add(key, o);
13375         }
13376         this.length++;
13377         this.items.splice(index, 0, o);
13378         if(typeof key != "undefined" && key != null){
13379             this.map[key] = o;
13380         }
13381         this.keys.splice(index, 0, key);
13382         this.fireEvent("add", index, o, key);
13383         return o;
13384     },
13385    
13386 /**
13387  * Removed an item from the collection.
13388  * @param {Object} o The item to remove.
13389  * @return {Object} The item removed.
13390  */
13391     remove : function(o){
13392         return this.removeAt(this.indexOf(o));
13393     },
13394    
13395 /**
13396  * Remove an item from a specified index in the collection.
13397  * @param {Number} index The index within the collection of the item to remove.
13398  */
13399     removeAt : function(index){
13400         if(index < this.length && index >= 0){
13401             this.length--;
13402             var o = this.items[index];
13403             this.items.splice(index, 1);
13404             var key = this.keys[index];
13405             if(typeof key != "undefined"){
13406                 delete this.map[key];
13407             }
13408             this.keys.splice(index, 1);
13409             this.fireEvent("remove", o, key);
13410         }
13411     },
13412    
13413 /**
13414  * Removed an item associated with the passed key fom the collection.
13415  * @param {String} key The key of the item to remove.
13416  */
13417     removeKey : function(key){
13418         return this.removeAt(this.indexOfKey(key));
13419     },
13420    
13421 /**
13422  * Returns the number of items in the collection.
13423  * @return {Number} the number of items in the collection.
13424  */
13425     getCount : function(){
13426         return this.length; 
13427     },
13428    
13429 /**
13430  * Returns index within the collection of the passed Object.
13431  * @param {Object} o The item to find the index of.
13432  * @return {Number} index of the item.
13433  */
13434     indexOf : function(o){
13435         if(!this.items.indexOf){
13436             for(var i = 0, len = this.items.length; i < len; i++){
13437                 if(this.items[i] == o) {
13438                     return i;
13439                 }
13440             }
13441             return -1;
13442         }else{
13443             return this.items.indexOf(o);
13444         }
13445     },
13446    
13447 /**
13448  * Returns index within the collection of the passed key.
13449  * @param {String} key The key to find the index of.
13450  * @return {Number} index of the key.
13451  */
13452     indexOfKey : function(key){
13453         if(!this.keys.indexOf){
13454             for(var i = 0, len = this.keys.length; i < len; i++){
13455                 if(this.keys[i] == key) {
13456                     return i;
13457                 }
13458             }
13459             return -1;
13460         }else{
13461             return this.keys.indexOf(key);
13462         }
13463     },
13464    
13465 /**
13466  * Returns the item associated with the passed key OR index. Key has priority over index.
13467  * @param {String/Number} key The key or index of the item.
13468  * @return {Object} The item associated with the passed key.
13469  */
13470     item : function(key){
13471         if (key === 'length') {
13472             return null;
13473         }
13474         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13475         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13476     },
13477     
13478 /**
13479  * Returns the item at the specified index.
13480  * @param {Number} index The index of the item.
13481  * @return {Object}
13482  */
13483     itemAt : function(index){
13484         return this.items[index];
13485     },
13486     
13487 /**
13488  * Returns the item associated with the passed key.
13489  * @param {String/Number} key The key of the item.
13490  * @return {Object} The item associated with the passed key.
13491  */
13492     key : function(key){
13493         return this.map[key];
13494     },
13495    
13496 /**
13497  * Returns true if the collection contains the passed Object as an item.
13498  * @param {Object} o  The Object to look for in the collection.
13499  * @return {Boolean} True if the collection contains the Object as an item.
13500  */
13501     contains : function(o){
13502         return this.indexOf(o) != -1;
13503     },
13504    
13505 /**
13506  * Returns true if the collection contains the passed Object as a key.
13507  * @param {String} key The key to look for in the collection.
13508  * @return {Boolean} True if the collection contains the Object as a key.
13509  */
13510     containsKey : function(key){
13511         return typeof this.map[key] != "undefined";
13512     },
13513    
13514 /**
13515  * Removes all items from the collection.
13516  */
13517     clear : function(){
13518         this.length = 0;
13519         this.items = [];
13520         this.keys = [];
13521         this.map = {};
13522         this.fireEvent("clear");
13523     },
13524    
13525 /**
13526  * Returns the first item in the collection.
13527  * @return {Object} the first item in the collection..
13528  */
13529     first : function(){
13530         return this.items[0]; 
13531     },
13532    
13533 /**
13534  * Returns the last item in the collection.
13535  * @return {Object} the last item in the collection..
13536  */
13537     last : function(){
13538         return this.items[this.length-1];   
13539     },
13540     
13541     _sort : function(property, dir, fn){
13542         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13543         fn = fn || function(a, b){
13544             return a-b;
13545         };
13546         var c = [], k = this.keys, items = this.items;
13547         for(var i = 0, len = items.length; i < len; i++){
13548             c[c.length] = {key: k[i], value: items[i], index: i};
13549         }
13550         c.sort(function(a, b){
13551             var v = fn(a[property], b[property]) * dsc;
13552             if(v == 0){
13553                 v = (a.index < b.index ? -1 : 1);
13554             }
13555             return v;
13556         });
13557         for(var i = 0, len = c.length; i < len; i++){
13558             items[i] = c[i].value;
13559             k[i] = c[i].key;
13560         }
13561         this.fireEvent("sort", this);
13562     },
13563     
13564     /**
13565      * Sorts this collection with the passed comparison function
13566      * @param {String} direction (optional) "ASC" or "DESC"
13567      * @param {Function} fn (optional) comparison function
13568      */
13569     sort : function(dir, fn){
13570         this._sort("value", dir, fn);
13571     },
13572     
13573     /**
13574      * Sorts this collection by keys
13575      * @param {String} direction (optional) "ASC" or "DESC"
13576      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13577      */
13578     keySort : function(dir, fn){
13579         this._sort("key", dir, fn || function(a, b){
13580             return String(a).toUpperCase()-String(b).toUpperCase();
13581         });
13582     },
13583     
13584     /**
13585      * Returns a range of items in this collection
13586      * @param {Number} startIndex (optional) defaults to 0
13587      * @param {Number} endIndex (optional) default to the last item
13588      * @return {Array} An array of items
13589      */
13590     getRange : function(start, end){
13591         var items = this.items;
13592         if(items.length < 1){
13593             return [];
13594         }
13595         start = start || 0;
13596         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13597         var r = [];
13598         if(start <= end){
13599             for(var i = start; i <= end; i++) {
13600                     r[r.length] = items[i];
13601             }
13602         }else{
13603             for(var i = start; i >= end; i--) {
13604                     r[r.length] = items[i];
13605             }
13606         }
13607         return r;
13608     },
13609         
13610     /**
13611      * Filter the <i>objects</i> in this collection by a specific property. 
13612      * Returns a new collection that has been filtered.
13613      * @param {String} property A property on your objects
13614      * @param {String/RegExp} value Either string that the property values 
13615      * should start with or a RegExp to test against the property
13616      * @return {MixedCollection} The new filtered collection
13617      */
13618     filter : function(property, value){
13619         if(!value.exec){ // not a regex
13620             value = String(value);
13621             if(value.length == 0){
13622                 return this.clone();
13623             }
13624             value = new RegExp("^" + Roo.escapeRe(value), "i");
13625         }
13626         return this.filterBy(function(o){
13627             return o && value.test(o[property]);
13628         });
13629         },
13630     
13631     /**
13632      * Filter by a function. * Returns a new collection that has been filtered.
13633      * The passed function will be called with each 
13634      * object in the collection. If the function returns true, the value is included 
13635      * otherwise it is filtered.
13636      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13637      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13638      * @return {MixedCollection} The new filtered collection
13639      */
13640     filterBy : function(fn, scope){
13641         var r = new Roo.util.MixedCollection();
13642         r.getKey = this.getKey;
13643         var k = this.keys, it = this.items;
13644         for(var i = 0, len = it.length; i < len; i++){
13645             if(fn.call(scope||this, it[i], k[i])){
13646                                 r.add(k[i], it[i]);
13647                         }
13648         }
13649         return r;
13650     },
13651     
13652     /**
13653      * Creates a duplicate of this collection
13654      * @return {MixedCollection}
13655      */
13656     clone : function(){
13657         var r = new Roo.util.MixedCollection();
13658         var k = this.keys, it = this.items;
13659         for(var i = 0, len = it.length; i < len; i++){
13660             r.add(k[i], it[i]);
13661         }
13662         r.getKey = this.getKey;
13663         return r;
13664     }
13665 });
13666 /**
13667  * Returns the item associated with the passed key or index.
13668  * @method
13669  * @param {String/Number} key The key or index of the item.
13670  * @return {Object} The item associated with the passed key.
13671  */
13672 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13673  * Based on:
13674  * Ext JS Library 1.1.1
13675  * Copyright(c) 2006-2007, Ext JS, LLC.
13676  *
13677  * Originally Released Under LGPL - original licence link has changed is not relivant.
13678  *
13679  * Fork - LGPL
13680  * <script type="text/javascript">
13681  */
13682 /**
13683  * @class Roo.util.JSON
13684  * Modified version of Douglas Crockford"s json.js that doesn"t
13685  * mess with the Object prototype 
13686  * http://www.json.org/js.html
13687  * @singleton
13688  */
13689 Roo.util.JSON = new (function(){
13690     var useHasOwn = {}.hasOwnProperty ? true : false;
13691     
13692     // crashes Safari in some instances
13693     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13694     
13695     var pad = function(n) {
13696         return n < 10 ? "0" + n : n;
13697     };
13698     
13699     var m = {
13700         "\b": '\\b',
13701         "\t": '\\t',
13702         "\n": '\\n',
13703         "\f": '\\f',
13704         "\r": '\\r',
13705         '"' : '\\"',
13706         "\\": '\\\\'
13707     };
13708
13709     var encodeString = function(s){
13710         if (/["\\\x00-\x1f]/.test(s)) {
13711             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13712                 var c = m[b];
13713                 if(c){
13714                     return c;
13715                 }
13716                 c = b.charCodeAt();
13717                 return "\\u00" +
13718                     Math.floor(c / 16).toString(16) +
13719                     (c % 16).toString(16);
13720             }) + '"';
13721         }
13722         return '"' + s + '"';
13723     };
13724     
13725     var encodeArray = function(o){
13726         var a = ["["], b, i, l = o.length, v;
13727             for (i = 0; i < l; i += 1) {
13728                 v = o[i];
13729                 switch (typeof v) {
13730                     case "undefined":
13731                     case "function":
13732                     case "unknown":
13733                         break;
13734                     default:
13735                         if (b) {
13736                             a.push(',');
13737                         }
13738                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13739                         b = true;
13740                 }
13741             }
13742             a.push("]");
13743             return a.join("");
13744     };
13745     
13746     var encodeDate = function(o){
13747         return '"' + o.getFullYear() + "-" +
13748                 pad(o.getMonth() + 1) + "-" +
13749                 pad(o.getDate()) + "T" +
13750                 pad(o.getHours()) + ":" +
13751                 pad(o.getMinutes()) + ":" +
13752                 pad(o.getSeconds()) + '"';
13753     };
13754     
13755     /**
13756      * Encodes an Object, Array or other value
13757      * @param {Mixed} o The variable to encode
13758      * @return {String} The JSON string
13759      */
13760     this.encode = function(o)
13761     {
13762         // should this be extended to fully wrap stringify..
13763         
13764         if(typeof o == "undefined" || o === null){
13765             return "null";
13766         }else if(o instanceof Array){
13767             return encodeArray(o);
13768         }else if(o instanceof Date){
13769             return encodeDate(o);
13770         }else if(typeof o == "string"){
13771             return encodeString(o);
13772         }else if(typeof o == "number"){
13773             return isFinite(o) ? String(o) : "null";
13774         }else if(typeof o == "boolean"){
13775             return String(o);
13776         }else {
13777             var a = ["{"], b, i, v;
13778             for (i in o) {
13779                 if(!useHasOwn || o.hasOwnProperty(i)) {
13780                     v = o[i];
13781                     switch (typeof v) {
13782                     case "undefined":
13783                     case "function":
13784                     case "unknown":
13785                         break;
13786                     default:
13787                         if(b){
13788                             a.push(',');
13789                         }
13790                         a.push(this.encode(i), ":",
13791                                 v === null ? "null" : this.encode(v));
13792                         b = true;
13793                     }
13794                 }
13795             }
13796             a.push("}");
13797             return a.join("");
13798         }
13799     };
13800     
13801     /**
13802      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13803      * @param {String} json The JSON string
13804      * @return {Object} The resulting object
13805      */
13806     this.decode = function(json){
13807         
13808         return  /** eval:var:json */ eval("(" + json + ')');
13809     };
13810 })();
13811 /** 
13812  * Shorthand for {@link Roo.util.JSON#encode}
13813  * @member Roo encode 
13814  * @method */
13815 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13816 /** 
13817  * Shorthand for {@link Roo.util.JSON#decode}
13818  * @member Roo decode 
13819  * @method */
13820 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13821 /*
13822  * Based on:
13823  * Ext JS Library 1.1.1
13824  * Copyright(c) 2006-2007, Ext JS, LLC.
13825  *
13826  * Originally Released Under LGPL - original licence link has changed is not relivant.
13827  *
13828  * Fork - LGPL
13829  * <script type="text/javascript">
13830  */
13831  
13832 /**
13833  * @class Roo.util.Format
13834  * Reusable data formatting functions
13835  * @singleton
13836  */
13837 Roo.util.Format = function(){
13838     var trimRe = /^\s+|\s+$/g;
13839     return {
13840         /**
13841          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13842          * @param {String} value The string to truncate
13843          * @param {Number} length The maximum length to allow before truncating
13844          * @return {String} The converted text
13845          */
13846         ellipsis : function(value, len){
13847             if(value && value.length > len){
13848                 return value.substr(0, len-3)+"...";
13849             }
13850             return value;
13851         },
13852
13853         /**
13854          * Checks a reference and converts it to empty string if it is undefined
13855          * @param {Mixed} value Reference to check
13856          * @return {Mixed} Empty string if converted, otherwise the original value
13857          */
13858         undef : function(value){
13859             return typeof value != "undefined" ? value : "";
13860         },
13861
13862         /**
13863          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13864          * @param {String} value The string to encode
13865          * @return {String} The encoded text
13866          */
13867         htmlEncode : function(value){
13868             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13869         },
13870
13871         /**
13872          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13873          * @param {String} value The string to decode
13874          * @return {String} The decoded text
13875          */
13876         htmlDecode : function(value){
13877             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13878         },
13879
13880         /**
13881          * Trims any whitespace from either side of a string
13882          * @param {String} value The text to trim
13883          * @return {String} The trimmed text
13884          */
13885         trim : function(value){
13886             return String(value).replace(trimRe, "");
13887         },
13888
13889         /**
13890          * Returns a substring from within an original string
13891          * @param {String} value The original text
13892          * @param {Number} start The start index of the substring
13893          * @param {Number} length The length of the substring
13894          * @return {String} The substring
13895          */
13896         substr : function(value, start, length){
13897             return String(value).substr(start, length);
13898         },
13899
13900         /**
13901          * Converts a string to all lower case letters
13902          * @param {String} value The text to convert
13903          * @return {String} The converted text
13904          */
13905         lowercase : function(value){
13906             return String(value).toLowerCase();
13907         },
13908
13909         /**
13910          * Converts a string to all upper case letters
13911          * @param {String} value The text to convert
13912          * @return {String} The converted text
13913          */
13914         uppercase : function(value){
13915             return String(value).toUpperCase();
13916         },
13917
13918         /**
13919          * Converts the first character only of a string to upper case
13920          * @param {String} value The text to convert
13921          * @return {String} The converted text
13922          */
13923         capitalize : function(value){
13924             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13925         },
13926
13927         // private
13928         call : function(value, fn){
13929             if(arguments.length > 2){
13930                 var args = Array.prototype.slice.call(arguments, 2);
13931                 args.unshift(value);
13932                  
13933                 return /** eval:var:value */  eval(fn).apply(window, args);
13934             }else{
13935                 /** eval:var:value */
13936                 return /** eval:var:value */ eval(fn).call(window, value);
13937             }
13938         },
13939
13940        
13941         /**
13942          * safer version of Math.toFixed..??/
13943          * @param {Number/String} value The numeric value to format
13944          * @param {Number/String} value Decimal places 
13945          * @return {String} The formatted currency string
13946          */
13947         toFixed : function(v, n)
13948         {
13949             // why not use to fixed - precision is buggered???
13950             if (!n) {
13951                 return Math.round(v-0);
13952             }
13953             var fact = Math.pow(10,n+1);
13954             v = (Math.round((v-0)*fact))/fact;
13955             var z = (''+fact).substring(2);
13956             if (v == Math.floor(v)) {
13957                 return Math.floor(v) + '.' + z;
13958             }
13959             
13960             // now just padd decimals..
13961             var ps = String(v).split('.');
13962             var fd = (ps[1] + z);
13963             var r = fd.substring(0,n); 
13964             var rm = fd.substring(n); 
13965             if (rm < 5) {
13966                 return ps[0] + '.' + r;
13967             }
13968             r*=1; // turn it into a number;
13969             r++;
13970             if (String(r).length != n) {
13971                 ps[0]*=1;
13972                 ps[0]++;
13973                 r = String(r).substring(1); // chop the end off.
13974             }
13975             
13976             return ps[0] + '.' + r;
13977              
13978         },
13979         
13980         /**
13981          * Format a number as US currency
13982          * @param {Number/String} value The numeric value to format
13983          * @return {String} The formatted currency string
13984          */
13985         usMoney : function(v){
13986             return '$' + Roo.util.Format.number(v);
13987         },
13988         
13989         /**
13990          * Format a number
13991          * eventually this should probably emulate php's number_format
13992          * @param {Number/String} value The numeric value to format
13993          * @param {Number} decimals number of decimal places
13994          * @param {String} delimiter for thousands (default comma)
13995          * @return {String} The formatted currency string
13996          */
13997         number : function(v, decimals, thousandsDelimiter)
13998         {
13999             // multiply and round.
14000             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14001             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14002             
14003             var mul = Math.pow(10, decimals);
14004             var zero = String(mul).substring(1);
14005             v = (Math.round((v-0)*mul))/mul;
14006             
14007             // if it's '0' number.. then
14008             
14009             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14010             v = String(v);
14011             var ps = v.split('.');
14012             var whole = ps[0];
14013             
14014             var r = /(\d+)(\d{3})/;
14015             // add comma's
14016             
14017             if(thousandsDelimiter.length != 0) {
14018                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14019             } 
14020             
14021             var sub = ps[1] ?
14022                     // has decimals..
14023                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14024                     // does not have decimals
14025                     (decimals ? ('.' + zero) : '');
14026             
14027             
14028             return whole + sub ;
14029         },
14030         
14031         /**
14032          * Parse a value into a formatted date using the specified format pattern.
14033          * @param {Mixed} value The value to format
14034          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14035          * @return {String} The formatted date string
14036          */
14037         date : function(v, format){
14038             if(!v){
14039                 return "";
14040             }
14041             if(!(v instanceof Date)){
14042                 v = new Date(Date.parse(v));
14043             }
14044             return v.dateFormat(format || Roo.util.Format.defaults.date);
14045         },
14046
14047         /**
14048          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14049          * @param {String} format Any valid date format string
14050          * @return {Function} The date formatting function
14051          */
14052         dateRenderer : function(format){
14053             return function(v){
14054                 return Roo.util.Format.date(v, format);  
14055             };
14056         },
14057
14058         // private
14059         stripTagsRE : /<\/?[^>]+>/gi,
14060         
14061         /**
14062          * Strips all HTML tags
14063          * @param {Mixed} value The text from which to strip tags
14064          * @return {String} The stripped text
14065          */
14066         stripTags : function(v){
14067             return !v ? v : String(v).replace(this.stripTagsRE, "");
14068         },
14069         
14070         /**
14071          * Size in Mb,Gb etc.
14072          * @param {Number} value The number to be formated
14073          * @param {number} decimals how many decimal places
14074          * @return {String} the formated string
14075          */
14076         size : function(value, decimals)
14077         {
14078             var sizes = ['b', 'k', 'M', 'G', 'T'];
14079             if (value == 0) {
14080                 return 0;
14081             }
14082             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14083             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14084         }
14085         
14086         
14087         
14088     };
14089 }();
14090 Roo.util.Format.defaults = {
14091     date : 'd/M/Y'
14092 };/*
14093  * Based on:
14094  * Ext JS Library 1.1.1
14095  * Copyright(c) 2006-2007, Ext JS, LLC.
14096  *
14097  * Originally Released Under LGPL - original licence link has changed is not relivant.
14098  *
14099  * Fork - LGPL
14100  * <script type="text/javascript">
14101  */
14102
14103
14104  
14105
14106 /**
14107  * @class Roo.MasterTemplate
14108  * @extends Roo.Template
14109  * Provides a template that can have child templates. The syntax is:
14110 <pre><code>
14111 var t = new Roo.MasterTemplate(
14112         '&lt;select name="{name}"&gt;',
14113                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14114         '&lt;/select&gt;'
14115 );
14116 t.add('options', {value: 'foo', text: 'bar'});
14117 // or you can add multiple child elements in one shot
14118 t.addAll('options', [
14119     {value: 'foo', text: 'bar'},
14120     {value: 'foo2', text: 'bar2'},
14121     {value: 'foo3', text: 'bar3'}
14122 ]);
14123 // then append, applying the master template values
14124 t.append('my-form', {name: 'my-select'});
14125 </code></pre>
14126 * A name attribute for the child template is not required if you have only one child
14127 * template or you want to refer to them by index.
14128  */
14129 Roo.MasterTemplate = function(){
14130     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14131     this.originalHtml = this.html;
14132     var st = {};
14133     var m, re = this.subTemplateRe;
14134     re.lastIndex = 0;
14135     var subIndex = 0;
14136     while(m = re.exec(this.html)){
14137         var name = m[1], content = m[2];
14138         st[subIndex] = {
14139             name: name,
14140             index: subIndex,
14141             buffer: [],
14142             tpl : new Roo.Template(content)
14143         };
14144         if(name){
14145             st[name] = st[subIndex];
14146         }
14147         st[subIndex].tpl.compile();
14148         st[subIndex].tpl.call = this.call.createDelegate(this);
14149         subIndex++;
14150     }
14151     this.subCount = subIndex;
14152     this.subs = st;
14153 };
14154 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14155     /**
14156     * The regular expression used to match sub templates
14157     * @type RegExp
14158     * @property
14159     */
14160     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14161
14162     /**
14163      * Applies the passed values to a child template.
14164      * @param {String/Number} name (optional) The name or index of the child template
14165      * @param {Array/Object} values The values to be applied to the template
14166      * @return {MasterTemplate} this
14167      */
14168      add : function(name, values){
14169         if(arguments.length == 1){
14170             values = arguments[0];
14171             name = 0;
14172         }
14173         var s = this.subs[name];
14174         s.buffer[s.buffer.length] = s.tpl.apply(values);
14175         return this;
14176     },
14177
14178     /**
14179      * Applies all the passed values to a child template.
14180      * @param {String/Number} name (optional) The name or index of the child template
14181      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14182      * @param {Boolean} reset (optional) True to reset the template first
14183      * @return {MasterTemplate} this
14184      */
14185     fill : function(name, values, reset){
14186         var a = arguments;
14187         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14188             values = a[0];
14189             name = 0;
14190             reset = a[1];
14191         }
14192         if(reset){
14193             this.reset();
14194         }
14195         for(var i = 0, len = values.length; i < len; i++){
14196             this.add(name, values[i]);
14197         }
14198         return this;
14199     },
14200
14201     /**
14202      * Resets the template for reuse
14203      * @return {MasterTemplate} this
14204      */
14205      reset : function(){
14206         var s = this.subs;
14207         for(var i = 0; i < this.subCount; i++){
14208             s[i].buffer = [];
14209         }
14210         return this;
14211     },
14212
14213     applyTemplate : function(values){
14214         var s = this.subs;
14215         var replaceIndex = -1;
14216         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14217             return s[++replaceIndex].buffer.join("");
14218         });
14219         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14220     },
14221
14222     apply : function(){
14223         return this.applyTemplate.apply(this, arguments);
14224     },
14225
14226     compile : function(){return this;}
14227 });
14228
14229 /**
14230  * Alias for fill().
14231  * @method
14232  */
14233 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14234  /**
14235  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14236  * var tpl = Roo.MasterTemplate.from('element-id');
14237  * @param {String/HTMLElement} el
14238  * @param {Object} config
14239  * @static
14240  */
14241 Roo.MasterTemplate.from = function(el, config){
14242     el = Roo.getDom(el);
14243     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14244 };/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255  
14256 /**
14257  * @class Roo.util.CSS
14258  * Utility class for manipulating CSS rules
14259  * @singleton
14260  */
14261 Roo.util.CSS = function(){
14262         var rules = null;
14263         var doc = document;
14264
14265     var camelRe = /(-[a-z])/gi;
14266     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14267
14268    return {
14269    /**
14270     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14271     * tag and appended to the HEAD of the document.
14272     * @param {String|Object} cssText The text containing the css rules
14273     * @param {String} id An id to add to the stylesheet for later removal
14274     * @return {StyleSheet}
14275     */
14276     createStyleSheet : function(cssText, id){
14277         var ss;
14278         var head = doc.getElementsByTagName("head")[0];
14279         var nrules = doc.createElement("style");
14280         nrules.setAttribute("type", "text/css");
14281         if(id){
14282             nrules.setAttribute("id", id);
14283         }
14284         if (typeof(cssText) != 'string') {
14285             // support object maps..
14286             // not sure if this a good idea.. 
14287             // perhaps it should be merged with the general css handling
14288             // and handle js style props.
14289             var cssTextNew = [];
14290             for(var n in cssText) {
14291                 var citems = [];
14292                 for(var k in cssText[n]) {
14293                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14294                 }
14295                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14296                 
14297             }
14298             cssText = cssTextNew.join("\n");
14299             
14300         }
14301        
14302        
14303        if(Roo.isIE){
14304            head.appendChild(nrules);
14305            ss = nrules.styleSheet;
14306            ss.cssText = cssText;
14307        }else{
14308            try{
14309                 nrules.appendChild(doc.createTextNode(cssText));
14310            }catch(e){
14311                nrules.cssText = cssText; 
14312            }
14313            head.appendChild(nrules);
14314            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14315        }
14316        this.cacheStyleSheet(ss);
14317        return ss;
14318    },
14319
14320    /**
14321     * Removes a style or link tag by id
14322     * @param {String} id The id of the tag
14323     */
14324    removeStyleSheet : function(id){
14325        var existing = doc.getElementById(id);
14326        if(existing){
14327            existing.parentNode.removeChild(existing);
14328        }
14329    },
14330
14331    /**
14332     * Dynamically swaps an existing stylesheet reference for a new one
14333     * @param {String} id The id of an existing link tag to remove
14334     * @param {String} url The href of the new stylesheet to include
14335     */
14336    swapStyleSheet : function(id, url){
14337        this.removeStyleSheet(id);
14338        var ss = doc.createElement("link");
14339        ss.setAttribute("rel", "stylesheet");
14340        ss.setAttribute("type", "text/css");
14341        ss.setAttribute("id", id);
14342        ss.setAttribute("href", url);
14343        doc.getElementsByTagName("head")[0].appendChild(ss);
14344    },
14345    
14346    /**
14347     * Refresh the rule cache if you have dynamically added stylesheets
14348     * @return {Object} An object (hash) of rules indexed by selector
14349     */
14350    refreshCache : function(){
14351        return this.getRules(true);
14352    },
14353
14354    // private
14355    cacheStyleSheet : function(stylesheet){
14356        if(!rules){
14357            rules = {};
14358        }
14359        try{// try catch for cross domain access issue
14360            var ssRules = stylesheet.cssRules || stylesheet.rules;
14361            for(var j = ssRules.length-1; j >= 0; --j){
14362                rules[ssRules[j].selectorText] = ssRules[j];
14363            }
14364        }catch(e){}
14365    },
14366    
14367    /**
14368     * Gets all css rules for the document
14369     * @param {Boolean} refreshCache true to refresh the internal cache
14370     * @return {Object} An object (hash) of rules indexed by selector
14371     */
14372    getRules : function(refreshCache){
14373                 if(rules == null || refreshCache){
14374                         rules = {};
14375                         var ds = doc.styleSheets;
14376                         for(var i =0, len = ds.length; i < len; i++){
14377                             try{
14378                         this.cacheStyleSheet(ds[i]);
14379                     }catch(e){} 
14380                 }
14381                 }
14382                 return rules;
14383         },
14384         
14385         /**
14386     * Gets an an individual CSS rule by selector(s)
14387     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14388     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14389     * @return {CSSRule} The CSS rule or null if one is not found
14390     */
14391    getRule : function(selector, refreshCache){
14392                 var rs = this.getRules(refreshCache);
14393                 if(!(selector instanceof Array)){
14394                     return rs[selector];
14395                 }
14396                 for(var i = 0; i < selector.length; i++){
14397                         if(rs[selector[i]]){
14398                                 return rs[selector[i]];
14399                         }
14400                 }
14401                 return null;
14402         },
14403         
14404         
14405         /**
14406     * Updates a rule property
14407     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14408     * @param {String} property The css property
14409     * @param {String} value The new value for the property
14410     * @return {Boolean} true If a rule was found and updated
14411     */
14412    updateRule : function(selector, property, value){
14413                 if(!(selector instanceof Array)){
14414                         var rule = this.getRule(selector);
14415                         if(rule){
14416                                 rule.style[property.replace(camelRe, camelFn)] = value;
14417                                 return true;
14418                         }
14419                 }else{
14420                         for(var i = 0; i < selector.length; i++){
14421                                 if(this.updateRule(selector[i], property, value)){
14422                                         return true;
14423                                 }
14424                         }
14425                 }
14426                 return false;
14427         }
14428    };   
14429 }();/*
14430  * Based on:
14431  * Ext JS Library 1.1.1
14432  * Copyright(c) 2006-2007, Ext JS, LLC.
14433  *
14434  * Originally Released Under LGPL - original licence link has changed is not relivant.
14435  *
14436  * Fork - LGPL
14437  * <script type="text/javascript">
14438  */
14439
14440  
14441
14442 /**
14443  * @class Roo.util.ClickRepeater
14444  * @extends Roo.util.Observable
14445  * 
14446  * A wrapper class which can be applied to any element. Fires a "click" event while the
14447  * mouse is pressed. The interval between firings may be specified in the config but
14448  * defaults to 10 milliseconds.
14449  * 
14450  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14451  * 
14452  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14453  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14454  * Similar to an autorepeat key delay.
14455  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14456  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14457  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14458  *           "interval" and "delay" are ignored. "immediate" is honored.
14459  * @cfg {Boolean} preventDefault True to prevent the default click event
14460  * @cfg {Boolean} stopDefault True to stop the default click event
14461  * 
14462  * @history
14463  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14464  *     2007-02-02 jvs Renamed to ClickRepeater
14465  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14466  *
14467  *  @constructor
14468  * @param {String/HTMLElement/Element} el The element to listen on
14469  * @param {Object} config
14470  **/
14471 Roo.util.ClickRepeater = function(el, config)
14472 {
14473     this.el = Roo.get(el);
14474     this.el.unselectable();
14475
14476     Roo.apply(this, config);
14477
14478     this.addEvents({
14479     /**
14480      * @event mousedown
14481      * Fires when the mouse button is depressed.
14482      * @param {Roo.util.ClickRepeater} this
14483      */
14484         "mousedown" : true,
14485     /**
14486      * @event click
14487      * Fires on a specified interval during the time the element is pressed.
14488      * @param {Roo.util.ClickRepeater} this
14489      */
14490         "click" : true,
14491     /**
14492      * @event mouseup
14493      * Fires when the mouse key is released.
14494      * @param {Roo.util.ClickRepeater} this
14495      */
14496         "mouseup" : true
14497     });
14498
14499     this.el.on("mousedown", this.handleMouseDown, this);
14500     if(this.preventDefault || this.stopDefault){
14501         this.el.on("click", function(e){
14502             if(this.preventDefault){
14503                 e.preventDefault();
14504             }
14505             if(this.stopDefault){
14506                 e.stopEvent();
14507             }
14508         }, this);
14509     }
14510
14511     // allow inline handler
14512     if(this.handler){
14513         this.on("click", this.handler,  this.scope || this);
14514     }
14515
14516     Roo.util.ClickRepeater.superclass.constructor.call(this);
14517 };
14518
14519 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14520     interval : 20,
14521     delay: 250,
14522     preventDefault : true,
14523     stopDefault : false,
14524     timer : 0,
14525
14526     // private
14527     handleMouseDown : function(){
14528         clearTimeout(this.timer);
14529         this.el.blur();
14530         if(this.pressClass){
14531             this.el.addClass(this.pressClass);
14532         }
14533         this.mousedownTime = new Date();
14534
14535         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14536         this.el.on("mouseout", this.handleMouseOut, this);
14537
14538         this.fireEvent("mousedown", this);
14539         this.fireEvent("click", this);
14540         
14541         this.timer = this.click.defer(this.delay || this.interval, this);
14542     },
14543
14544     // private
14545     click : function(){
14546         this.fireEvent("click", this);
14547         this.timer = this.click.defer(this.getInterval(), this);
14548     },
14549
14550     // private
14551     getInterval: function(){
14552         if(!this.accelerate){
14553             return this.interval;
14554         }
14555         var pressTime = this.mousedownTime.getElapsed();
14556         if(pressTime < 500){
14557             return 400;
14558         }else if(pressTime < 1700){
14559             return 320;
14560         }else if(pressTime < 2600){
14561             return 250;
14562         }else if(pressTime < 3500){
14563             return 180;
14564         }else if(pressTime < 4400){
14565             return 140;
14566         }else if(pressTime < 5300){
14567             return 80;
14568         }else if(pressTime < 6200){
14569             return 50;
14570         }else{
14571             return 10;
14572         }
14573     },
14574
14575     // private
14576     handleMouseOut : function(){
14577         clearTimeout(this.timer);
14578         if(this.pressClass){
14579             this.el.removeClass(this.pressClass);
14580         }
14581         this.el.on("mouseover", this.handleMouseReturn, this);
14582     },
14583
14584     // private
14585     handleMouseReturn : function(){
14586         this.el.un("mouseover", this.handleMouseReturn);
14587         if(this.pressClass){
14588             this.el.addClass(this.pressClass);
14589         }
14590         this.click();
14591     },
14592
14593     // private
14594     handleMouseUp : function(){
14595         clearTimeout(this.timer);
14596         this.el.un("mouseover", this.handleMouseReturn);
14597         this.el.un("mouseout", this.handleMouseOut);
14598         Roo.get(document).un("mouseup", this.handleMouseUp);
14599         this.el.removeClass(this.pressClass);
14600         this.fireEvent("mouseup", this);
14601     }
14602 });/**
14603  * @class Roo.util.Clipboard
14604  * @static
14605  * 
14606  * Clipboard UTILS
14607  * 
14608  **/
14609 Roo.util.Clipboard = {
14610     /**
14611      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14612      * @param {String} text to copy to clipboard
14613      */
14614     write : function(text) {
14615         // navigator clipboard api needs a secure context (https)
14616         if (navigator.clipboard && window.isSecureContext) {
14617             // navigator clipboard api method'
14618             navigator.clipboard.writeText(text);
14619             return ;
14620         } 
14621         // text area method
14622         var ta = document.createElement("textarea");
14623         ta.value = text;
14624         // make the textarea out of viewport
14625         ta.style.position = "fixed";
14626         ta.style.left = "-999999px";
14627         ta.style.top = "-999999px";
14628         document.body.appendChild(ta);
14629         ta.focus();
14630         ta.select();
14631         document.execCommand('copy');
14632         (function() {
14633             ta.remove();
14634         }).defer(100);
14635         
14636     }
14637         
14638 }
14639     /*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649
14650  
14651 /**
14652  * @class Roo.KeyNav
14653  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14654  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14655  * way to implement custom navigation schemes for any UI component.</p>
14656  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14657  * pageUp, pageDown, del, home, end.  Usage:</p>
14658  <pre><code>
14659 var nav = new Roo.KeyNav("my-element", {
14660     "left" : function(e){
14661         this.moveLeft(e.ctrlKey);
14662     },
14663     "right" : function(e){
14664         this.moveRight(e.ctrlKey);
14665     },
14666     "enter" : function(e){
14667         this.save();
14668     },
14669     scope : this
14670 });
14671 </code></pre>
14672  * @constructor
14673  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14674  * @param {Object} config The config
14675  */
14676 Roo.KeyNav = function(el, config){
14677     this.el = Roo.get(el);
14678     Roo.apply(this, config);
14679     if(!this.disabled){
14680         this.disabled = true;
14681         this.enable();
14682     }
14683 };
14684
14685 Roo.KeyNav.prototype = {
14686     /**
14687      * @cfg {Boolean} disabled
14688      * True to disable this KeyNav instance (defaults to false)
14689      */
14690     disabled : false,
14691     /**
14692      * @cfg {String} defaultEventAction
14693      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14694      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14695      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14696      */
14697     defaultEventAction: "stopEvent",
14698     /**
14699      * @cfg {Boolean} forceKeyDown
14700      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14701      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14702      * handle keydown instead of keypress.
14703      */
14704     forceKeyDown : false,
14705
14706     // private
14707     prepareEvent : function(e){
14708         var k = e.getKey();
14709         var h = this.keyToHandler[k];
14710         //if(h && this[h]){
14711         //    e.stopPropagation();
14712         //}
14713         if(Roo.isSafari && h && k >= 37 && k <= 40){
14714             e.stopEvent();
14715         }
14716     },
14717
14718     // private
14719     relay : function(e){
14720         var k = e.getKey();
14721         var h = this.keyToHandler[k];
14722         if(h && this[h]){
14723             if(this.doRelay(e, this[h], h) !== true){
14724                 e[this.defaultEventAction]();
14725             }
14726         }
14727     },
14728
14729     // private
14730     doRelay : function(e, h, hname){
14731         return h.call(this.scope || this, e);
14732     },
14733
14734     // possible handlers
14735     enter : false,
14736     left : false,
14737     right : false,
14738     up : false,
14739     down : false,
14740     tab : false,
14741     esc : false,
14742     pageUp : false,
14743     pageDown : false,
14744     del : false,
14745     home : false,
14746     end : false,
14747
14748     // quick lookup hash
14749     keyToHandler : {
14750         37 : "left",
14751         39 : "right",
14752         38 : "up",
14753         40 : "down",
14754         33 : "pageUp",
14755         34 : "pageDown",
14756         46 : "del",
14757         36 : "home",
14758         35 : "end",
14759         13 : "enter",
14760         27 : "esc",
14761         9  : "tab"
14762     },
14763
14764         /**
14765          * Enable this KeyNav
14766          */
14767         enable: function(){
14768                 if(this.disabled){
14769             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14770             // the EventObject will normalize Safari automatically
14771             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14772                 this.el.on("keydown", this.relay,  this);
14773             }else{
14774                 this.el.on("keydown", this.prepareEvent,  this);
14775                 this.el.on("keypress", this.relay,  this);
14776             }
14777                     this.disabled = false;
14778                 }
14779         },
14780
14781         /**
14782          * Disable this KeyNav
14783          */
14784         disable: function(){
14785                 if(!this.disabled){
14786                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14787                 this.el.un("keydown", this.relay);
14788             }else{
14789                 this.el.un("keydown", this.prepareEvent);
14790                 this.el.un("keypress", this.relay);
14791             }
14792                     this.disabled = true;
14793                 }
14794         }
14795 };/*
14796  * Based on:
14797  * Ext JS Library 1.1.1
14798  * Copyright(c) 2006-2007, Ext JS, LLC.
14799  *
14800  * Originally Released Under LGPL - original licence link has changed is not relivant.
14801  *
14802  * Fork - LGPL
14803  * <script type="text/javascript">
14804  */
14805
14806  
14807 /**
14808  * @class Roo.KeyMap
14809  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14810  * The constructor accepts the same config object as defined by {@link #addBinding}.
14811  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14812  * combination it will call the function with this signature (if the match is a multi-key
14813  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14814  * A KeyMap can also handle a string representation of keys.<br />
14815  * Usage:
14816  <pre><code>
14817 // map one key by key code
14818 var map = new Roo.KeyMap("my-element", {
14819     key: 13, // or Roo.EventObject.ENTER
14820     fn: myHandler,
14821     scope: myObject
14822 });
14823
14824 // map multiple keys to one action by string
14825 var map = new Roo.KeyMap("my-element", {
14826     key: "a\r\n\t",
14827     fn: myHandler,
14828     scope: myObject
14829 });
14830
14831 // map multiple keys to multiple actions by strings and array of codes
14832 var map = new Roo.KeyMap("my-element", [
14833     {
14834         key: [10,13],
14835         fn: function(){ alert("Return was pressed"); }
14836     }, {
14837         key: "abc",
14838         fn: function(){ alert('a, b or c was pressed'); }
14839     }, {
14840         key: "\t",
14841         ctrl:true,
14842         shift:true,
14843         fn: function(){ alert('Control + shift + tab was pressed.'); }
14844     }
14845 ]);
14846 </code></pre>
14847  * <b>Note: A KeyMap starts enabled</b>
14848  * @constructor
14849  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14850  * @param {Object} config The config (see {@link #addBinding})
14851  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14852  */
14853 Roo.KeyMap = function(el, config, eventName){
14854     this.el  = Roo.get(el);
14855     this.eventName = eventName || "keydown";
14856     this.bindings = [];
14857     if(config){
14858         this.addBinding(config);
14859     }
14860     this.enable();
14861 };
14862
14863 Roo.KeyMap.prototype = {
14864     /**
14865      * True to stop the event from bubbling and prevent the default browser action if the
14866      * key was handled by the KeyMap (defaults to false)
14867      * @type Boolean
14868      */
14869     stopEvent : false,
14870
14871     /**
14872      * Add a new binding to this KeyMap. The following config object properties are supported:
14873      * <pre>
14874 Property    Type             Description
14875 ----------  ---------------  ----------------------------------------------------------------------
14876 key         String/Array     A single keycode or an array of keycodes to handle
14877 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14878 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14879 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14880 fn          Function         The function to call when KeyMap finds the expected key combination
14881 scope       Object           The scope of the callback function
14882 </pre>
14883      *
14884      * Usage:
14885      * <pre><code>
14886 // Create a KeyMap
14887 var map = new Roo.KeyMap(document, {
14888     key: Roo.EventObject.ENTER,
14889     fn: handleKey,
14890     scope: this
14891 });
14892
14893 //Add a new binding to the existing KeyMap later
14894 map.addBinding({
14895     key: 'abc',
14896     shift: true,
14897     fn: handleKey,
14898     scope: this
14899 });
14900 </code></pre>
14901      * @param {Object/Array} config A single KeyMap config or an array of configs
14902      */
14903         addBinding : function(config){
14904         if(config instanceof Array){
14905             for(var i = 0, len = config.length; i < len; i++){
14906                 this.addBinding(config[i]);
14907             }
14908             return;
14909         }
14910         var keyCode = config.key,
14911             shift = config.shift, 
14912             ctrl = config.ctrl, 
14913             alt = config.alt,
14914             fn = config.fn,
14915             scope = config.scope;
14916         if(typeof keyCode == "string"){
14917             var ks = [];
14918             var keyString = keyCode.toUpperCase();
14919             for(var j = 0, len = keyString.length; j < len; j++){
14920                 ks.push(keyString.charCodeAt(j));
14921             }
14922             keyCode = ks;
14923         }
14924         var keyArray = keyCode instanceof Array;
14925         var handler = function(e){
14926             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14927                 var k = e.getKey();
14928                 if(keyArray){
14929                     for(var i = 0, len = keyCode.length; i < len; i++){
14930                         if(keyCode[i] == k){
14931                           if(this.stopEvent){
14932                               e.stopEvent();
14933                           }
14934                           fn.call(scope || window, k, e);
14935                           return;
14936                         }
14937                     }
14938                 }else{
14939                     if(k == keyCode){
14940                         if(this.stopEvent){
14941                            e.stopEvent();
14942                         }
14943                         fn.call(scope || window, k, e);
14944                     }
14945                 }
14946             }
14947         };
14948         this.bindings.push(handler);  
14949         },
14950
14951     /**
14952      * Shorthand for adding a single key listener
14953      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14954      * following options:
14955      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14956      * @param {Function} fn The function to call
14957      * @param {Object} scope (optional) The scope of the function
14958      */
14959     on : function(key, fn, scope){
14960         var keyCode, shift, ctrl, alt;
14961         if(typeof key == "object" && !(key instanceof Array)){
14962             keyCode = key.key;
14963             shift = key.shift;
14964             ctrl = key.ctrl;
14965             alt = key.alt;
14966         }else{
14967             keyCode = key;
14968         }
14969         this.addBinding({
14970             key: keyCode,
14971             shift: shift,
14972             ctrl: ctrl,
14973             alt: alt,
14974             fn: fn,
14975             scope: scope
14976         })
14977     },
14978
14979     // private
14980     handleKeyDown : function(e){
14981             if(this.enabled){ //just in case
14982             var b = this.bindings;
14983             for(var i = 0, len = b.length; i < len; i++){
14984                 b[i].call(this, e);
14985             }
14986             }
14987         },
14988         
14989         /**
14990          * Returns true if this KeyMap is enabled
14991          * @return {Boolean} 
14992          */
14993         isEnabled : function(){
14994             return this.enabled;  
14995         },
14996         
14997         /**
14998          * Enables this KeyMap
14999          */
15000         enable: function(){
15001                 if(!this.enabled){
15002                     this.el.on(this.eventName, this.handleKeyDown, this);
15003                     this.enabled = true;
15004                 }
15005         },
15006
15007         /**
15008          * Disable this KeyMap
15009          */
15010         disable: function(){
15011                 if(this.enabled){
15012                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15013                     this.enabled = false;
15014                 }
15015         }
15016 };/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027  
15028 /**
15029  * @class Roo.util.TextMetrics
15030  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15031  * wide, in pixels, a given block of text will be.
15032  * @singleton
15033  */
15034 Roo.util.TextMetrics = function(){
15035     var shared;
15036     return {
15037         /**
15038          * Measures the size of the specified text
15039          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15040          * that can affect the size of the rendered text
15041          * @param {String} text The text to measure
15042          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15043          * in order to accurately measure the text height
15044          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15045          */
15046         measure : function(el, text, fixedWidth){
15047             if(!shared){
15048                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15049             }
15050             shared.bind(el);
15051             shared.setFixedWidth(fixedWidth || 'auto');
15052             return shared.getSize(text);
15053         },
15054
15055         /**
15056          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15057          * the overhead of multiple calls to initialize the style properties on each measurement.
15058          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15059          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15060          * in order to accurately measure the text height
15061          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15062          */
15063         createInstance : function(el, fixedWidth){
15064             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15065         }
15066     };
15067 }();
15068
15069  
15070
15071 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15072     var ml = new Roo.Element(document.createElement('div'));
15073     document.body.appendChild(ml.dom);
15074     ml.position('absolute');
15075     ml.setLeftTop(-1000, -1000);
15076     ml.hide();
15077
15078     if(fixedWidth){
15079         ml.setWidth(fixedWidth);
15080     }
15081      
15082     var instance = {
15083         /**
15084          * Returns the size of the specified text based on the internal element's style and width properties
15085          * @memberOf Roo.util.TextMetrics.Instance#
15086          * @param {String} text The text to measure
15087          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15088          */
15089         getSize : function(text){
15090             ml.update(text);
15091             var s = ml.getSize();
15092             ml.update('');
15093             return s;
15094         },
15095
15096         /**
15097          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15098          * that can affect the size of the rendered text
15099          * @memberOf Roo.util.TextMetrics.Instance#
15100          * @param {String/HTMLElement} el The element, dom node or id
15101          */
15102         bind : function(el){
15103             ml.setStyle(
15104                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15105             );
15106         },
15107
15108         /**
15109          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15110          * to set a fixed width in order to accurately measure the text height.
15111          * @memberOf Roo.util.TextMetrics.Instance#
15112          * @param {Number} width The width to set on the element
15113          */
15114         setFixedWidth : function(width){
15115             ml.setWidth(width);
15116         },
15117
15118         /**
15119          * Returns the measured width of the specified text
15120          * @memberOf Roo.util.TextMetrics.Instance#
15121          * @param {String} text The text to measure
15122          * @return {Number} width The width in pixels
15123          */
15124         getWidth : function(text){
15125             ml.dom.style.width = 'auto';
15126             return this.getSize(text).width;
15127         },
15128
15129         /**
15130          * Returns the measured height of the specified text.  For multiline text, be sure to call
15131          * {@link #setFixedWidth} if necessary.
15132          * @memberOf Roo.util.TextMetrics.Instance#
15133          * @param {String} text The text to measure
15134          * @return {Number} height The height in pixels
15135          */
15136         getHeight : function(text){
15137             return this.getSize(text).height;
15138         }
15139     };
15140
15141     instance.bind(bindTo);
15142
15143     return instance;
15144 };
15145
15146 // backwards compat
15147 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15148  * Based on:
15149  * Ext JS Library 1.1.1
15150  * Copyright(c) 2006-2007, Ext JS, LLC.
15151  *
15152  * Originally Released Under LGPL - original licence link has changed is not relivant.
15153  *
15154  * Fork - LGPL
15155  * <script type="text/javascript">
15156  */
15157
15158 /**
15159  * @class Roo.state.Provider
15160  * Abstract base class for state provider implementations. This class provides methods
15161  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15162  * Provider interface.
15163  */
15164 Roo.state.Provider = function(){
15165     /**
15166      * @event statechange
15167      * Fires when a state change occurs.
15168      * @param {Provider} this This state provider
15169      * @param {String} key The state key which was changed
15170      * @param {String} value The encoded value for the state
15171      */
15172     this.addEvents({
15173         "statechange": true
15174     });
15175     this.state = {};
15176     Roo.state.Provider.superclass.constructor.call(this);
15177 };
15178 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15179     /**
15180      * Returns the current value for a key
15181      * @param {String} name The key name
15182      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15183      * @return {Mixed} The state data
15184      */
15185     get : function(name, defaultValue){
15186         return typeof this.state[name] == "undefined" ?
15187             defaultValue : this.state[name];
15188     },
15189     
15190     /**
15191      * Clears a value from the state
15192      * @param {String} name The key name
15193      */
15194     clear : function(name){
15195         delete this.state[name];
15196         this.fireEvent("statechange", this, name, null);
15197     },
15198     
15199     /**
15200      * Sets the value for a key
15201      * @param {String} name The key name
15202      * @param {Mixed} value The value to set
15203      */
15204     set : function(name, value){
15205         this.state[name] = value;
15206         this.fireEvent("statechange", this, name, value);
15207     },
15208     
15209     /**
15210      * Decodes a string previously encoded with {@link #encodeValue}.
15211      * @param {String} value The value to decode
15212      * @return {Mixed} The decoded value
15213      */
15214     decodeValue : function(cookie){
15215         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15216         var matches = re.exec(unescape(cookie));
15217         if(!matches || !matches[1]) {
15218             return; // non state cookie
15219         }
15220         var type = matches[1];
15221         var v = matches[2];
15222         switch(type){
15223             case "n":
15224                 return parseFloat(v);
15225             case "d":
15226                 return new Date(Date.parse(v));
15227             case "b":
15228                 return (v == "1");
15229             case "a":
15230                 var all = [];
15231                 var values = v.split("^");
15232                 for(var i = 0, len = values.length; i < len; i++){
15233                     all.push(this.decodeValue(values[i]));
15234                 }
15235                 return all;
15236            case "o":
15237                 var all = {};
15238                 var values = v.split("^");
15239                 for(var i = 0, len = values.length; i < len; i++){
15240                     var kv = values[i].split("=");
15241                     all[kv[0]] = this.decodeValue(kv[1]);
15242                 }
15243                 return all;
15244            default:
15245                 return v;
15246         }
15247     },
15248     
15249     /**
15250      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15251      * @param {Mixed} value The value to encode
15252      * @return {String} The encoded value
15253      */
15254     encodeValue : function(v){
15255         var enc;
15256         if(typeof v == "number"){
15257             enc = "n:" + v;
15258         }else if(typeof v == "boolean"){
15259             enc = "b:" + (v ? "1" : "0");
15260         }else if(v instanceof Date){
15261             enc = "d:" + v.toGMTString();
15262         }else if(v instanceof Array){
15263             var flat = "";
15264             for(var i = 0, len = v.length; i < len; i++){
15265                 flat += this.encodeValue(v[i]);
15266                 if(i != len-1) {
15267                     flat += "^";
15268                 }
15269             }
15270             enc = "a:" + flat;
15271         }else if(typeof v == "object"){
15272             var flat = "";
15273             for(var key in v){
15274                 if(typeof v[key] != "function"){
15275                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15276                 }
15277             }
15278             enc = "o:" + flat.substring(0, flat.length-1);
15279         }else{
15280             enc = "s:" + v;
15281         }
15282         return escape(enc);        
15283     }
15284 });
15285
15286 /*
15287  * Based on:
15288  * Ext JS Library 1.1.1
15289  * Copyright(c) 2006-2007, Ext JS, LLC.
15290  *
15291  * Originally Released Under LGPL - original licence link has changed is not relivant.
15292  *
15293  * Fork - LGPL
15294  * <script type="text/javascript">
15295  */
15296 /**
15297  * @class Roo.state.Manager
15298  * This is the global state manager. By default all components that are "state aware" check this class
15299  * for state information if you don't pass them a custom state provider. In order for this class
15300  * to be useful, it must be initialized with a provider when your application initializes.
15301  <pre><code>
15302 // in your initialization function
15303 init : function(){
15304    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15305    ...
15306    // supposed you have a {@link Roo.BorderLayout}
15307    var layout = new Roo.BorderLayout(...);
15308    layout.restoreState();
15309    // or a {Roo.BasicDialog}
15310    var dialog = new Roo.BasicDialog(...);
15311    dialog.restoreState();
15312  </code></pre>
15313  * @singleton
15314  */
15315 Roo.state.Manager = function(){
15316     var provider = new Roo.state.Provider();
15317     
15318     return {
15319         /**
15320          * Configures the default state provider for your application
15321          * @param {Provider} stateProvider The state provider to set
15322          */
15323         setProvider : function(stateProvider){
15324             provider = stateProvider;
15325         },
15326         
15327         /**
15328          * Returns the current value for a key
15329          * @param {String} name The key name
15330          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15331          * @return {Mixed} The state data
15332          */
15333         get : function(key, defaultValue){
15334             return provider.get(key, defaultValue);
15335         },
15336         
15337         /**
15338          * Sets the value for a key
15339          * @param {String} name The key name
15340          * @param {Mixed} value The state data
15341          */
15342          set : function(key, value){
15343             provider.set(key, value);
15344         },
15345         
15346         /**
15347          * Clears a value from the state
15348          * @param {String} name The key name
15349          */
15350         clear : function(key){
15351             provider.clear(key);
15352         },
15353         
15354         /**
15355          * Gets the currently configured state provider
15356          * @return {Provider} The state provider
15357          */
15358         getProvider : function(){
15359             return provider;
15360         }
15361     };
15362 }();
15363 /*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373 /**
15374  * @class Roo.state.CookieProvider
15375  * @extends Roo.state.Provider
15376  * The default Provider implementation which saves state via cookies.
15377  * <br />Usage:
15378  <pre><code>
15379    var cp = new Roo.state.CookieProvider({
15380        path: "/cgi-bin/",
15381        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15382        domain: "roojs.com"
15383    })
15384    Roo.state.Manager.setProvider(cp);
15385  </code></pre>
15386  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15387  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15388  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15389  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15390  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15391  * domain the page is running on including the 'www' like 'www.roojs.com')
15392  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15393  * @constructor
15394  * Create a new CookieProvider
15395  * @param {Object} config The configuration object
15396  */
15397 Roo.state.CookieProvider = function(config){
15398     Roo.state.CookieProvider.superclass.constructor.call(this);
15399     this.path = "/";
15400     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15401     this.domain = null;
15402     this.secure = false;
15403     Roo.apply(this, config);
15404     this.state = this.readCookies();
15405 };
15406
15407 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15408     // private
15409     set : function(name, value){
15410         if(typeof value == "undefined" || value === null){
15411             this.clear(name);
15412             return;
15413         }
15414         this.setCookie(name, value);
15415         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15416     },
15417
15418     // private
15419     clear : function(name){
15420         this.clearCookie(name);
15421         Roo.state.CookieProvider.superclass.clear.call(this, name);
15422     },
15423
15424     // private
15425     readCookies : function(){
15426         var cookies = {};
15427         var c = document.cookie + ";";
15428         var re = /\s?(.*?)=(.*?);/g;
15429         var matches;
15430         while((matches = re.exec(c)) != null){
15431             var name = matches[1];
15432             var value = matches[2];
15433             if(name && name.substring(0,3) == "ys-"){
15434                 cookies[name.substr(3)] = this.decodeValue(value);
15435             }
15436         }
15437         return cookies;
15438     },
15439
15440     // private
15441     setCookie : function(name, value){
15442         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15443            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15444            ((this.path == null) ? "" : ("; path=" + this.path)) +
15445            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15446            ((this.secure == true) ? "; secure" : "");
15447     },
15448
15449     // private
15450     clearCookie : function(name){
15451         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15452            ((this.path == null) ? "" : ("; path=" + this.path)) +
15453            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15454            ((this.secure == true) ? "; secure" : "");
15455     }
15456 });/*
15457  * Based on:
15458  * Ext JS Library 1.1.1
15459  * Copyright(c) 2006-2007, Ext JS, LLC.
15460  *
15461  * Originally Released Under LGPL - original licence link has changed is not relivant.
15462  *
15463  * Fork - LGPL
15464  * <script type="text/javascript">
15465  */
15466  
15467
15468 /**
15469  * @class Roo.ComponentMgr
15470  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15471  * @singleton
15472  */
15473 Roo.ComponentMgr = function(){
15474     var all = new Roo.util.MixedCollection();
15475
15476     return {
15477         /**
15478          * Registers a component.
15479          * @param {Roo.Component} c The component
15480          */
15481         register : function(c){
15482             all.add(c);
15483         },
15484
15485         /**
15486          * Unregisters a component.
15487          * @param {Roo.Component} c The component
15488          */
15489         unregister : function(c){
15490             all.remove(c);
15491         },
15492
15493         /**
15494          * Returns a component by id
15495          * @param {String} id The component id
15496          */
15497         get : function(id){
15498             return all.get(id);
15499         },
15500
15501         /**
15502          * Registers a function that will be called when a specified component is added to ComponentMgr
15503          * @param {String} id The component id
15504          * @param {Funtction} fn The callback function
15505          * @param {Object} scope The scope of the callback
15506          */
15507         onAvailable : function(id, fn, scope){
15508             all.on("add", function(index, o){
15509                 if(o.id == id){
15510                     fn.call(scope || o, o);
15511                     all.un("add", fn, scope);
15512                 }
15513             });
15514         }
15515     };
15516 }();/*
15517  * Based on:
15518  * Ext JS Library 1.1.1
15519  * Copyright(c) 2006-2007, Ext JS, LLC.
15520  *
15521  * Originally Released Under LGPL - original licence link has changed is not relivant.
15522  *
15523  * Fork - LGPL
15524  * <script type="text/javascript">
15525  */
15526  
15527 /**
15528  * @class Roo.Component
15529  * @extends Roo.util.Observable
15530  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15531  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15532  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15533  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15534  * All visual components (widgets) that require rendering into a layout should subclass Component.
15535  * @constructor
15536  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15537  * 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
15538  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15539  */
15540 Roo.Component = function(config){
15541     config = config || {};
15542     if(config.tagName || config.dom || typeof config == "string"){ // element object
15543         config = {el: config, id: config.id || config};
15544     }
15545     this.initialConfig = config;
15546
15547     Roo.apply(this, config);
15548     this.addEvents({
15549         /**
15550          * @event disable
15551          * Fires after the component is disabled.
15552              * @param {Roo.Component} this
15553              */
15554         disable : true,
15555         /**
15556          * @event enable
15557          * Fires after the component is enabled.
15558              * @param {Roo.Component} this
15559              */
15560         enable : true,
15561         /**
15562          * @event beforeshow
15563          * Fires before the component is shown.  Return false to stop the show.
15564              * @param {Roo.Component} this
15565              */
15566         beforeshow : true,
15567         /**
15568          * @event show
15569          * Fires after the component is shown.
15570              * @param {Roo.Component} this
15571              */
15572         show : true,
15573         /**
15574          * @event beforehide
15575          * Fires before the component is hidden. Return false to stop the hide.
15576              * @param {Roo.Component} this
15577              */
15578         beforehide : true,
15579         /**
15580          * @event hide
15581          * Fires after the component is hidden.
15582              * @param {Roo.Component} this
15583              */
15584         hide : true,
15585         /**
15586          * @event beforerender
15587          * Fires before the component is rendered. Return false to stop the render.
15588              * @param {Roo.Component} this
15589              */
15590         beforerender : true,
15591         /**
15592          * @event render
15593          * Fires after the component is rendered.
15594              * @param {Roo.Component} this
15595              */
15596         render : true,
15597         /**
15598          * @event beforedestroy
15599          * Fires before the component is destroyed. Return false to stop the destroy.
15600              * @param {Roo.Component} this
15601              */
15602         beforedestroy : true,
15603         /**
15604          * @event destroy
15605          * Fires after the component is destroyed.
15606              * @param {Roo.Component} this
15607              */
15608         destroy : true
15609     });
15610     if(!this.id){
15611         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15612     }
15613     Roo.ComponentMgr.register(this);
15614     Roo.Component.superclass.constructor.call(this);
15615     this.initComponent();
15616     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15617         this.render(this.renderTo);
15618         delete this.renderTo;
15619     }
15620 };
15621
15622 /** @private */
15623 Roo.Component.AUTO_ID = 1000;
15624
15625 Roo.extend(Roo.Component, Roo.util.Observable, {
15626     /**
15627      * @scope Roo.Component.prototype
15628      * @type {Boolean}
15629      * true if this component is hidden. Read-only.
15630      */
15631     hidden : false,
15632     /**
15633      * @type {Boolean}
15634      * true if this component is disabled. Read-only.
15635      */
15636     disabled : false,
15637     /**
15638      * @type {Boolean}
15639      * true if this component has been rendered. Read-only.
15640      */
15641     rendered : false,
15642     
15643     /** @cfg {String} disableClass
15644      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15645      */
15646     disabledClass : "x-item-disabled",
15647         /** @cfg {Boolean} allowDomMove
15648          * Whether the component can move the Dom node when rendering (defaults to true).
15649          */
15650     allowDomMove : true,
15651     /** @cfg {String} hideMode (display|visibility)
15652      * How this component should hidden. Supported values are
15653      * "visibility" (css visibility), "offsets" (negative offset position) and
15654      * "display" (css display) - defaults to "display".
15655      */
15656     hideMode: 'display',
15657
15658     /** @private */
15659     ctype : "Roo.Component",
15660
15661     /**
15662      * @cfg {String} actionMode 
15663      * which property holds the element that used for  hide() / show() / disable() / enable()
15664      * default is 'el' for forms you probably want to set this to fieldEl 
15665      */
15666     actionMode : "el",
15667
15668     /** @private */
15669     getActionEl : function(){
15670         return this[this.actionMode];
15671     },
15672
15673     initComponent : Roo.emptyFn,
15674     /**
15675      * If this is a lazy rendering component, render it to its container element.
15676      * @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.
15677      */
15678     render : function(container, position){
15679         
15680         if(this.rendered){
15681             return this;
15682         }
15683         
15684         if(this.fireEvent("beforerender", this) === false){
15685             return false;
15686         }
15687         
15688         if(!container && this.el){
15689             this.el = Roo.get(this.el);
15690             container = this.el.dom.parentNode;
15691             this.allowDomMove = false;
15692         }
15693         this.container = Roo.get(container);
15694         this.rendered = true;
15695         if(position !== undefined){
15696             if(typeof position == 'number'){
15697                 position = this.container.dom.childNodes[position];
15698             }else{
15699                 position = Roo.getDom(position);
15700             }
15701         }
15702         this.onRender(this.container, position || null);
15703         if(this.cls){
15704             this.el.addClass(this.cls);
15705             delete this.cls;
15706         }
15707         if(this.style){
15708             this.el.applyStyles(this.style);
15709             delete this.style;
15710         }
15711         this.fireEvent("render", this);
15712         this.afterRender(this.container);
15713         if(this.hidden){
15714             this.hide();
15715         }
15716         if(this.disabled){
15717             this.disable();
15718         }
15719
15720         return this;
15721         
15722     },
15723
15724     /** @private */
15725     // default function is not really useful
15726     onRender : function(ct, position){
15727         if(this.el){
15728             this.el = Roo.get(this.el);
15729             if(this.allowDomMove !== false){
15730                 ct.dom.insertBefore(this.el.dom, position);
15731             }
15732         }
15733     },
15734
15735     /** @private */
15736     getAutoCreate : function(){
15737         var cfg = typeof this.autoCreate == "object" ?
15738                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15739         if(this.id && !cfg.id){
15740             cfg.id = this.id;
15741         }
15742         return cfg;
15743     },
15744
15745     /** @private */
15746     afterRender : Roo.emptyFn,
15747
15748     /**
15749      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15750      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15751      */
15752     destroy : function(){
15753         if(this.fireEvent("beforedestroy", this) !== false){
15754             this.purgeListeners();
15755             this.beforeDestroy();
15756             if(this.rendered){
15757                 this.el.removeAllListeners();
15758                 this.el.remove();
15759                 if(this.actionMode == "container"){
15760                     this.container.remove();
15761                 }
15762             }
15763             this.onDestroy();
15764             Roo.ComponentMgr.unregister(this);
15765             this.fireEvent("destroy", this);
15766         }
15767     },
15768
15769         /** @private */
15770     beforeDestroy : function(){
15771
15772     },
15773
15774         /** @private */
15775         onDestroy : function(){
15776
15777     },
15778
15779     /**
15780      * Returns the underlying {@link Roo.Element}.
15781      * @return {Roo.Element} The element
15782      */
15783     getEl : function(){
15784         return this.el;
15785     },
15786
15787     /**
15788      * Returns the id of this component.
15789      * @return {String}
15790      */
15791     getId : function(){
15792         return this.id;
15793     },
15794
15795     /**
15796      * Try to focus this component.
15797      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15798      * @return {Roo.Component} this
15799      */
15800     focus : function(selectText){
15801         if(this.rendered){
15802             this.el.focus();
15803             if(selectText === true){
15804                 this.el.dom.select();
15805             }
15806         }
15807         return this;
15808     },
15809
15810     /** @private */
15811     blur : function(){
15812         if(this.rendered){
15813             this.el.blur();
15814         }
15815         return this;
15816     },
15817
15818     /**
15819      * Disable this component.
15820      * @return {Roo.Component} this
15821      */
15822     disable : function(){
15823         if(this.rendered){
15824             this.onDisable();
15825         }
15826         this.disabled = true;
15827         this.fireEvent("disable", this);
15828         return this;
15829     },
15830
15831         // private
15832     onDisable : function(){
15833         this.getActionEl().addClass(this.disabledClass);
15834         this.el.dom.disabled = true;
15835     },
15836
15837     /**
15838      * Enable this component.
15839      * @return {Roo.Component} this
15840      */
15841     enable : function(){
15842         if(this.rendered){
15843             this.onEnable();
15844         }
15845         this.disabled = false;
15846         this.fireEvent("enable", this);
15847         return this;
15848     },
15849
15850         // private
15851     onEnable : function(){
15852         this.getActionEl().removeClass(this.disabledClass);
15853         this.el.dom.disabled = false;
15854     },
15855
15856     /**
15857      * Convenience function for setting disabled/enabled by boolean.
15858      * @param {Boolean} disabled
15859      */
15860     setDisabled : function(disabled){
15861         this[disabled ? "disable" : "enable"]();
15862     },
15863
15864     /**
15865      * Show this component.
15866      * @return {Roo.Component} this
15867      */
15868     show: function(){
15869         if(this.fireEvent("beforeshow", this) !== false){
15870             this.hidden = false;
15871             if(this.rendered){
15872                 this.onShow();
15873             }
15874             this.fireEvent("show", this);
15875         }
15876         return this;
15877     },
15878
15879     // private
15880     onShow : function(){
15881         var ae = this.getActionEl();
15882         if(this.hideMode == 'visibility'){
15883             ae.dom.style.visibility = "visible";
15884         }else if(this.hideMode == 'offsets'){
15885             ae.removeClass('x-hidden');
15886         }else{
15887             ae.dom.style.display = "";
15888         }
15889     },
15890
15891     /**
15892      * Hide this component.
15893      * @return {Roo.Component} this
15894      */
15895     hide: function(){
15896         if(this.fireEvent("beforehide", this) !== false){
15897             this.hidden = true;
15898             if(this.rendered){
15899                 this.onHide();
15900             }
15901             this.fireEvent("hide", this);
15902         }
15903         return this;
15904     },
15905
15906     // private
15907     onHide : function(){
15908         var ae = this.getActionEl();
15909         if(this.hideMode == 'visibility'){
15910             ae.dom.style.visibility = "hidden";
15911         }else if(this.hideMode == 'offsets'){
15912             ae.addClass('x-hidden');
15913         }else{
15914             ae.dom.style.display = "none";
15915         }
15916     },
15917
15918     /**
15919      * Convenience function to hide or show this component by boolean.
15920      * @param {Boolean} visible True to show, false to hide
15921      * @return {Roo.Component} this
15922      */
15923     setVisible: function(visible){
15924         if(visible) {
15925             this.show();
15926         }else{
15927             this.hide();
15928         }
15929         return this;
15930     },
15931
15932     /**
15933      * Returns true if this component is visible.
15934      */
15935     isVisible : function(){
15936         return this.getActionEl().isVisible();
15937     },
15938
15939     cloneConfig : function(overrides){
15940         overrides = overrides || {};
15941         var id = overrides.id || Roo.id();
15942         var cfg = Roo.applyIf(overrides, this.initialConfig);
15943         cfg.id = id; // prevent dup id
15944         return new this.constructor(cfg);
15945     }
15946 });/*
15947  * Based on:
15948  * Ext JS Library 1.1.1
15949  * Copyright(c) 2006-2007, Ext JS, LLC.
15950  *
15951  * Originally Released Under LGPL - original licence link has changed is not relivant.
15952  *
15953  * Fork - LGPL
15954  * <script type="text/javascript">
15955  */
15956
15957 /**
15958  * @class Roo.BoxComponent
15959  * @extends Roo.Component
15960  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15961  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15962  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15963  * layout containers.
15964  * @constructor
15965  * @param {Roo.Element/String/Object} config The configuration options.
15966  */
15967 Roo.BoxComponent = function(config){
15968     Roo.Component.call(this, config);
15969     this.addEvents({
15970         /**
15971          * @event resize
15972          * Fires after the component is resized.
15973              * @param {Roo.Component} this
15974              * @param {Number} adjWidth The box-adjusted width that was set
15975              * @param {Number} adjHeight The box-adjusted height that was set
15976              * @param {Number} rawWidth The width that was originally specified
15977              * @param {Number} rawHeight The height that was originally specified
15978              */
15979         resize : true,
15980         /**
15981          * @event move
15982          * Fires after the component is moved.
15983              * @param {Roo.Component} this
15984              * @param {Number} x The new x position
15985              * @param {Number} y The new y position
15986              */
15987         move : true
15988     });
15989 };
15990
15991 Roo.extend(Roo.BoxComponent, Roo.Component, {
15992     // private, set in afterRender to signify that the component has been rendered
15993     boxReady : false,
15994     // private, used to defer height settings to subclasses
15995     deferHeight: false,
15996     /** @cfg {Number} width
15997      * width (optional) size of component
15998      */
15999      /** @cfg {Number} height
16000      * height (optional) size of component
16001      */
16002      
16003     /**
16004      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16005      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16006      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16007      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16008      * @return {Roo.BoxComponent} this
16009      */
16010     setSize : function(w, h){
16011         // support for standard size objects
16012         if(typeof w == 'object'){
16013             h = w.height;
16014             w = w.width;
16015         }
16016         // not rendered
16017         if(!this.boxReady){
16018             this.width = w;
16019             this.height = h;
16020             return this;
16021         }
16022
16023         // prevent recalcs when not needed
16024         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16025             return this;
16026         }
16027         this.lastSize = {width: w, height: h};
16028
16029         var adj = this.adjustSize(w, h);
16030         var aw = adj.width, ah = adj.height;
16031         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16032             var rz = this.getResizeEl();
16033             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16034                 rz.setSize(aw, ah);
16035             }else if(!this.deferHeight && ah !== undefined){
16036                 rz.setHeight(ah);
16037             }else if(aw !== undefined){
16038                 rz.setWidth(aw);
16039             }
16040             this.onResize(aw, ah, w, h);
16041             this.fireEvent('resize', this, aw, ah, w, h);
16042         }
16043         return this;
16044     },
16045
16046     /**
16047      * Gets the current size of the component's underlying element.
16048      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16049      */
16050     getSize : function(){
16051         return this.el.getSize();
16052     },
16053
16054     /**
16055      * Gets the current XY position of the component's underlying element.
16056      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16057      * @return {Array} The XY position of the element (e.g., [100, 200])
16058      */
16059     getPosition : function(local){
16060         if(local === true){
16061             return [this.el.getLeft(true), this.el.getTop(true)];
16062         }
16063         return this.xy || this.el.getXY();
16064     },
16065
16066     /**
16067      * Gets the current box measurements of the component's underlying element.
16068      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16069      * @returns {Object} box An object in the format {x, y, width, height}
16070      */
16071     getBox : function(local){
16072         var s = this.el.getSize();
16073         if(local){
16074             s.x = this.el.getLeft(true);
16075             s.y = this.el.getTop(true);
16076         }else{
16077             var xy = this.xy || this.el.getXY();
16078             s.x = xy[0];
16079             s.y = xy[1];
16080         }
16081         return s;
16082     },
16083
16084     /**
16085      * Sets the current box measurements of the component's underlying element.
16086      * @param {Object} box An object in the format {x, y, width, height}
16087      * @returns {Roo.BoxComponent} this
16088      */
16089     updateBox : function(box){
16090         this.setSize(box.width, box.height);
16091         this.setPagePosition(box.x, box.y);
16092         return this;
16093     },
16094
16095     // protected
16096     getResizeEl : function(){
16097         return this.resizeEl || this.el;
16098     },
16099
16100     // protected
16101     getPositionEl : function(){
16102         return this.positionEl || this.el;
16103     },
16104
16105     /**
16106      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16107      * This method fires the move event.
16108      * @param {Number} left The new left
16109      * @param {Number} top The new top
16110      * @returns {Roo.BoxComponent} this
16111      */
16112     setPosition : function(x, y){
16113         this.x = x;
16114         this.y = y;
16115         if(!this.boxReady){
16116             return this;
16117         }
16118         var adj = this.adjustPosition(x, y);
16119         var ax = adj.x, ay = adj.y;
16120
16121         var el = this.getPositionEl();
16122         if(ax !== undefined || ay !== undefined){
16123             if(ax !== undefined && ay !== undefined){
16124                 el.setLeftTop(ax, ay);
16125             }else if(ax !== undefined){
16126                 el.setLeft(ax);
16127             }else if(ay !== undefined){
16128                 el.setTop(ay);
16129             }
16130             this.onPosition(ax, ay);
16131             this.fireEvent('move', this, ax, ay);
16132         }
16133         return this;
16134     },
16135
16136     /**
16137      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16138      * This method fires the move event.
16139      * @param {Number} x The new x position
16140      * @param {Number} y The new y position
16141      * @returns {Roo.BoxComponent} this
16142      */
16143     setPagePosition : function(x, y){
16144         this.pageX = x;
16145         this.pageY = y;
16146         if(!this.boxReady){
16147             return;
16148         }
16149         if(x === undefined || y === undefined){ // cannot translate undefined points
16150             return;
16151         }
16152         var p = this.el.translatePoints(x, y);
16153         this.setPosition(p.left, p.top);
16154         return this;
16155     },
16156
16157     // private
16158     onRender : function(ct, position){
16159         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16160         if(this.resizeEl){
16161             this.resizeEl = Roo.get(this.resizeEl);
16162         }
16163         if(this.positionEl){
16164             this.positionEl = Roo.get(this.positionEl);
16165         }
16166     },
16167
16168     // private
16169     afterRender : function(){
16170         Roo.BoxComponent.superclass.afterRender.call(this);
16171         this.boxReady = true;
16172         this.setSize(this.width, this.height);
16173         if(this.x || this.y){
16174             this.setPosition(this.x, this.y);
16175         }
16176         if(this.pageX || this.pageY){
16177             this.setPagePosition(this.pageX, this.pageY);
16178         }
16179     },
16180
16181     /**
16182      * Force the component's size to recalculate based on the underlying element's current height and width.
16183      * @returns {Roo.BoxComponent} this
16184      */
16185     syncSize : function(){
16186         delete this.lastSize;
16187         this.setSize(this.el.getWidth(), this.el.getHeight());
16188         return this;
16189     },
16190
16191     /**
16192      * Called after the component is resized, this method is empty by default but can be implemented by any
16193      * subclass that needs to perform custom logic after a resize occurs.
16194      * @param {Number} adjWidth The box-adjusted width that was set
16195      * @param {Number} adjHeight The box-adjusted height that was set
16196      * @param {Number} rawWidth The width that was originally specified
16197      * @param {Number} rawHeight The height that was originally specified
16198      */
16199     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16200
16201     },
16202
16203     /**
16204      * Called after the component is moved, this method is empty by default but can be implemented by any
16205      * subclass that needs to perform custom logic after a move occurs.
16206      * @param {Number} x The new x position
16207      * @param {Number} y The new y position
16208      */
16209     onPosition : function(x, y){
16210
16211     },
16212
16213     // private
16214     adjustSize : function(w, h){
16215         if(this.autoWidth){
16216             w = 'auto';
16217         }
16218         if(this.autoHeight){
16219             h = 'auto';
16220         }
16221         return {width : w, height: h};
16222     },
16223
16224     // private
16225     adjustPosition : function(x, y){
16226         return {x : x, y: y};
16227     }
16228 });/*
16229  * Based on:
16230  * Ext JS Library 1.1.1
16231  * Copyright(c) 2006-2007, Ext JS, LLC.
16232  *
16233  * Originally Released Under LGPL - original licence link has changed is not relivant.
16234  *
16235  * Fork - LGPL
16236  * <script type="text/javascript">
16237  */
16238  (function(){ 
16239 /**
16240  * @class Roo.Layer
16241  * @extends Roo.Element
16242  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16243  * automatic maintaining of shadow/shim positions.
16244  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16245  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16246  * you can pass a string with a CSS class name. False turns off the shadow.
16247  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16248  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16249  * @cfg {String} cls CSS class to add to the element
16250  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16251  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16252  * @constructor
16253  * @param {Object} config An object with config options.
16254  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16255  */
16256
16257 Roo.Layer = function(config, existingEl){
16258     config = config || {};
16259     var dh = Roo.DomHelper;
16260     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16261     if(existingEl){
16262         this.dom = Roo.getDom(existingEl);
16263     }
16264     if(!this.dom){
16265         var o = config.dh || {tag: "div", cls: "x-layer"};
16266         this.dom = dh.append(pel, o);
16267     }
16268     if(config.cls){
16269         this.addClass(config.cls);
16270     }
16271     this.constrain = config.constrain !== false;
16272     this.visibilityMode = Roo.Element.VISIBILITY;
16273     if(config.id){
16274         this.id = this.dom.id = config.id;
16275     }else{
16276         this.id = Roo.id(this.dom);
16277     }
16278     this.zindex = config.zindex || this.getZIndex();
16279     this.position("absolute", this.zindex);
16280     if(config.shadow){
16281         this.shadowOffset = config.shadowOffset || 4;
16282         this.shadow = new Roo.Shadow({
16283             offset : this.shadowOffset,
16284             mode : config.shadow
16285         });
16286     }else{
16287         this.shadowOffset = 0;
16288     }
16289     this.useShim = config.shim !== false && Roo.useShims;
16290     this.useDisplay = config.useDisplay;
16291     this.hide();
16292 };
16293
16294 var supr = Roo.Element.prototype;
16295
16296 // shims are shared among layer to keep from having 100 iframes
16297 var shims = [];
16298
16299 Roo.extend(Roo.Layer, Roo.Element, {
16300
16301     getZIndex : function(){
16302         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16303     },
16304
16305     getShim : function(){
16306         if(!this.useShim){
16307             return null;
16308         }
16309         if(this.shim){
16310             return this.shim;
16311         }
16312         var shim = shims.shift();
16313         if(!shim){
16314             shim = this.createShim();
16315             shim.enableDisplayMode('block');
16316             shim.dom.style.display = 'none';
16317             shim.dom.style.visibility = 'visible';
16318         }
16319         var pn = this.dom.parentNode;
16320         if(shim.dom.parentNode != pn){
16321             pn.insertBefore(shim.dom, this.dom);
16322         }
16323         shim.setStyle('z-index', this.getZIndex()-2);
16324         this.shim = shim;
16325         return shim;
16326     },
16327
16328     hideShim : function(){
16329         if(this.shim){
16330             this.shim.setDisplayed(false);
16331             shims.push(this.shim);
16332             delete this.shim;
16333         }
16334     },
16335
16336     disableShadow : function(){
16337         if(this.shadow){
16338             this.shadowDisabled = true;
16339             this.shadow.hide();
16340             this.lastShadowOffset = this.shadowOffset;
16341             this.shadowOffset = 0;
16342         }
16343     },
16344
16345     enableShadow : function(show){
16346         if(this.shadow){
16347             this.shadowDisabled = false;
16348             this.shadowOffset = this.lastShadowOffset;
16349             delete this.lastShadowOffset;
16350             if(show){
16351                 this.sync(true);
16352             }
16353         }
16354     },
16355
16356     // private
16357     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16358     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16359     sync : function(doShow){
16360         var sw = this.shadow;
16361         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16362             var sh = this.getShim();
16363
16364             var w = this.getWidth(),
16365                 h = this.getHeight();
16366
16367             var l = this.getLeft(true),
16368                 t = this.getTop(true);
16369
16370             if(sw && !this.shadowDisabled){
16371                 if(doShow && !sw.isVisible()){
16372                     sw.show(this);
16373                 }else{
16374                     sw.realign(l, t, w, h);
16375                 }
16376                 if(sh){
16377                     if(doShow){
16378                        sh.show();
16379                     }
16380                     // fit the shim behind the shadow, so it is shimmed too
16381                     var a = sw.adjusts, s = sh.dom.style;
16382                     s.left = (Math.min(l, l+a.l))+"px";
16383                     s.top = (Math.min(t, t+a.t))+"px";
16384                     s.width = (w+a.w)+"px";
16385                     s.height = (h+a.h)+"px";
16386                 }
16387             }else if(sh){
16388                 if(doShow){
16389                    sh.show();
16390                 }
16391                 sh.setSize(w, h);
16392                 sh.setLeftTop(l, t);
16393             }
16394             
16395         }
16396     },
16397
16398     // private
16399     destroy : function(){
16400         this.hideShim();
16401         if(this.shadow){
16402             this.shadow.hide();
16403         }
16404         this.removeAllListeners();
16405         var pn = this.dom.parentNode;
16406         if(pn){
16407             pn.removeChild(this.dom);
16408         }
16409         Roo.Element.uncache(this.id);
16410     },
16411
16412     remove : function(){
16413         this.destroy();
16414     },
16415
16416     // private
16417     beginUpdate : function(){
16418         this.updating = true;
16419     },
16420
16421     // private
16422     endUpdate : function(){
16423         this.updating = false;
16424         this.sync(true);
16425     },
16426
16427     // private
16428     hideUnders : function(negOffset){
16429         if(this.shadow){
16430             this.shadow.hide();
16431         }
16432         this.hideShim();
16433     },
16434
16435     // private
16436     constrainXY : function(){
16437         if(this.constrain){
16438             var vw = Roo.lib.Dom.getViewWidth(),
16439                 vh = Roo.lib.Dom.getViewHeight();
16440             var s = Roo.get(document).getScroll();
16441
16442             var xy = this.getXY();
16443             var x = xy[0], y = xy[1];   
16444             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16445             // only move it if it needs it
16446             var moved = false;
16447             // first validate right/bottom
16448             if((x + w) > vw+s.left){
16449                 x = vw - w - this.shadowOffset;
16450                 moved = true;
16451             }
16452             if((y + h) > vh+s.top){
16453                 y = vh - h - this.shadowOffset;
16454                 moved = true;
16455             }
16456             // then make sure top/left isn't negative
16457             if(x < s.left){
16458                 x = s.left;
16459                 moved = true;
16460             }
16461             if(y < s.top){
16462                 y = s.top;
16463                 moved = true;
16464             }
16465             if(moved){
16466                 if(this.avoidY){
16467                     var ay = this.avoidY;
16468                     if(y <= ay && (y+h) >= ay){
16469                         y = ay-h-5;   
16470                     }
16471                 }
16472                 xy = [x, y];
16473                 this.storeXY(xy);
16474                 supr.setXY.call(this, xy);
16475                 this.sync();
16476             }
16477         }
16478     },
16479
16480     isVisible : function(){
16481         return this.visible;    
16482     },
16483
16484     // private
16485     showAction : function(){
16486         this.visible = true; // track visibility to prevent getStyle calls
16487         if(this.useDisplay === true){
16488             this.setDisplayed("");
16489         }else if(this.lastXY){
16490             supr.setXY.call(this, this.lastXY);
16491         }else if(this.lastLT){
16492             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16493         }
16494     },
16495
16496     // private
16497     hideAction : function(){
16498         this.visible = false;
16499         if(this.useDisplay === true){
16500             this.setDisplayed(false);
16501         }else{
16502             this.setLeftTop(-10000,-10000);
16503         }
16504     },
16505
16506     // overridden Element method
16507     setVisible : function(v, a, d, c, e){
16508         if(v){
16509             this.showAction();
16510         }
16511         if(a && v){
16512             var cb = function(){
16513                 this.sync(true);
16514                 if(c){
16515                     c();
16516                 }
16517             }.createDelegate(this);
16518             supr.setVisible.call(this, true, true, d, cb, e);
16519         }else{
16520             if(!v){
16521                 this.hideUnders(true);
16522             }
16523             var cb = c;
16524             if(a){
16525                 cb = function(){
16526                     this.hideAction();
16527                     if(c){
16528                         c();
16529                     }
16530                 }.createDelegate(this);
16531             }
16532             supr.setVisible.call(this, v, a, d, cb, e);
16533             if(v){
16534                 this.sync(true);
16535             }else if(!a){
16536                 this.hideAction();
16537             }
16538         }
16539     },
16540
16541     storeXY : function(xy){
16542         delete this.lastLT;
16543         this.lastXY = xy;
16544     },
16545
16546     storeLeftTop : function(left, top){
16547         delete this.lastXY;
16548         this.lastLT = [left, top];
16549     },
16550
16551     // private
16552     beforeFx : function(){
16553         this.beforeAction();
16554         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16555     },
16556
16557     // private
16558     afterFx : function(){
16559         Roo.Layer.superclass.afterFx.apply(this, arguments);
16560         this.sync(this.isVisible());
16561     },
16562
16563     // private
16564     beforeAction : function(){
16565         if(!this.updating && this.shadow){
16566             this.shadow.hide();
16567         }
16568     },
16569
16570     // overridden Element method
16571     setLeft : function(left){
16572         this.storeLeftTop(left, this.getTop(true));
16573         supr.setLeft.apply(this, arguments);
16574         this.sync();
16575     },
16576
16577     setTop : function(top){
16578         this.storeLeftTop(this.getLeft(true), top);
16579         supr.setTop.apply(this, arguments);
16580         this.sync();
16581     },
16582
16583     setLeftTop : function(left, top){
16584         this.storeLeftTop(left, top);
16585         supr.setLeftTop.apply(this, arguments);
16586         this.sync();
16587     },
16588
16589     setXY : function(xy, a, d, c, e){
16590         this.fixDisplay();
16591         this.beforeAction();
16592         this.storeXY(xy);
16593         var cb = this.createCB(c);
16594         supr.setXY.call(this, xy, a, d, cb, e);
16595         if(!a){
16596             cb();
16597         }
16598     },
16599
16600     // private
16601     createCB : function(c){
16602         var el = this;
16603         return function(){
16604             el.constrainXY();
16605             el.sync(true);
16606             if(c){
16607                 c();
16608             }
16609         };
16610     },
16611
16612     // overridden Element method
16613     setX : function(x, a, d, c, e){
16614         this.setXY([x, this.getY()], a, d, c, e);
16615     },
16616
16617     // overridden Element method
16618     setY : function(y, a, d, c, e){
16619         this.setXY([this.getX(), y], a, d, c, e);
16620     },
16621
16622     // overridden Element method
16623     setSize : function(w, h, a, d, c, e){
16624         this.beforeAction();
16625         var cb = this.createCB(c);
16626         supr.setSize.call(this, w, h, a, d, cb, e);
16627         if(!a){
16628             cb();
16629         }
16630     },
16631
16632     // overridden Element method
16633     setWidth : function(w, a, d, c, e){
16634         this.beforeAction();
16635         var cb = this.createCB(c);
16636         supr.setWidth.call(this, w, a, d, cb, e);
16637         if(!a){
16638             cb();
16639         }
16640     },
16641
16642     // overridden Element method
16643     setHeight : function(h, a, d, c, e){
16644         this.beforeAction();
16645         var cb = this.createCB(c);
16646         supr.setHeight.call(this, h, a, d, cb, e);
16647         if(!a){
16648             cb();
16649         }
16650     },
16651
16652     // overridden Element method
16653     setBounds : function(x, y, w, h, a, d, c, e){
16654         this.beforeAction();
16655         var cb = this.createCB(c);
16656         if(!a){
16657             this.storeXY([x, y]);
16658             supr.setXY.call(this, [x, y]);
16659             supr.setSize.call(this, w, h, a, d, cb, e);
16660             cb();
16661         }else{
16662             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16663         }
16664         return this;
16665     },
16666     
16667     /**
16668      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16669      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16670      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16671      * @param {Number} zindex The new z-index to set
16672      * @return {this} The Layer
16673      */
16674     setZIndex : function(zindex){
16675         this.zindex = zindex;
16676         this.setStyle("z-index", zindex + 2);
16677         if(this.shadow){
16678             this.shadow.setZIndex(zindex + 1);
16679         }
16680         if(this.shim){
16681             this.shim.setStyle("z-index", zindex);
16682         }
16683     }
16684 });
16685 })();/*
16686  * Original code for Roojs - LGPL
16687  * <script type="text/javascript">
16688  */
16689  
16690 /**
16691  * @class Roo.XComponent
16692  * A delayed Element creator...
16693  * Or a way to group chunks of interface together.
16694  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16695  *  used in conjunction with XComponent.build() it will create an instance of each element,
16696  *  then call addxtype() to build the User interface.
16697  * 
16698  * Mypart.xyx = new Roo.XComponent({
16699
16700     parent : 'Mypart.xyz', // empty == document.element.!!
16701     order : '001',
16702     name : 'xxxx'
16703     region : 'xxxx'
16704     disabled : function() {} 
16705      
16706     tree : function() { // return an tree of xtype declared components
16707         var MODULE = this;
16708         return 
16709         {
16710             xtype : 'NestedLayoutPanel',
16711             // technicall
16712         }
16713      ]
16714  *})
16715  *
16716  *
16717  * It can be used to build a big heiracy, with parent etc.
16718  * or you can just use this to render a single compoent to a dom element
16719  * MYPART.render(Roo.Element | String(id) | dom_element )
16720  *
16721  *
16722  * Usage patterns.
16723  *
16724  * Classic Roo
16725  *
16726  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16727  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16728  *
16729  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16730  *
16731  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16732  * - if mulitple topModules exist, the last one is defined as the top module.
16733  *
16734  * Embeded Roo
16735  * 
16736  * When the top level or multiple modules are to embedded into a existing HTML page,
16737  * the parent element can container '#id' of the element where the module will be drawn.
16738  *
16739  * Bootstrap Roo
16740  *
16741  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16742  * it relies more on a include mechanism, where sub modules are included into an outer page.
16743  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16744  * 
16745  * Bootstrap Roo Included elements
16746  *
16747  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16748  * hence confusing the component builder as it thinks there are multiple top level elements. 
16749  *
16750  * String Over-ride & Translations
16751  *
16752  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16753  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16754  * are needed. @see Roo.XComponent.overlayString  
16755  * 
16756  * 
16757  * 
16758  * @extends Roo.util.Observable
16759  * @constructor
16760  * @param cfg {Object} configuration of component
16761  * 
16762  */
16763 Roo.XComponent = function(cfg) {
16764     Roo.apply(this, cfg);
16765     this.addEvents({ 
16766         /**
16767              * @event built
16768              * Fires when this the componnt is built
16769              * @param {Roo.XComponent} c the component
16770              */
16771         'built' : true
16772         
16773     });
16774     this.region = this.region || 'center'; // default..
16775     Roo.XComponent.register(this);
16776     this.modules = false;
16777     this.el = false; // where the layout goes..
16778     
16779     
16780 }
16781 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16782     /**
16783      * @property el
16784      * The created element (with Roo.factory())
16785      * @type {Roo.Layout}
16786      */
16787     el  : false,
16788     
16789     /**
16790      * @property el
16791      * for BC  - use el in new code
16792      * @type {Roo.Layout}
16793      */
16794     panel : false,
16795     
16796     /**
16797      * @property layout
16798      * for BC  - use el in new code
16799      * @type {Roo.Layout}
16800      */
16801     layout : false,
16802     
16803      /**
16804      * @cfg {Function|boolean} disabled
16805      * If this module is disabled by some rule, return true from the funtion
16806      */
16807     disabled : false,
16808     
16809     /**
16810      * @cfg {String} parent 
16811      * Name of parent element which it get xtype added to..
16812      */
16813     parent: false,
16814     
16815     /**
16816      * @cfg {String} order
16817      * Used to set the order in which elements are created (usefull for multiple tabs)
16818      */
16819     
16820     order : false,
16821     /**
16822      * @cfg {String} name
16823      * String to display while loading.
16824      */
16825     name : false,
16826     /**
16827      * @cfg {String} region
16828      * Region to render component to (defaults to center)
16829      */
16830     region : 'center',
16831     
16832     /**
16833      * @cfg {Array} items
16834      * A single item array - the first element is the root of the tree..
16835      * It's done this way to stay compatible with the Xtype system...
16836      */
16837     items : false,
16838     
16839     /**
16840      * @property _tree
16841      * The method that retuns the tree of parts that make up this compoennt 
16842      * @type {function}
16843      */
16844     _tree  : false,
16845     
16846      /**
16847      * render
16848      * render element to dom or tree
16849      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16850      */
16851     
16852     render : function(el)
16853     {
16854         
16855         el = el || false;
16856         var hp = this.parent ? 1 : 0;
16857         Roo.debug &&  Roo.log(this);
16858         
16859         var tree = this._tree ? this._tree() : this.tree();
16860
16861         
16862         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16863             // if parent is a '#.....' string, then let's use that..
16864             var ename = this.parent.substr(1);
16865             this.parent = false;
16866             Roo.debug && Roo.log(ename);
16867             switch (ename) {
16868                 case 'bootstrap-body':
16869                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16870                         // this is the BorderLayout standard?
16871                        this.parent = { el : true };
16872                        break;
16873                     }
16874                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16875                         // need to insert stuff...
16876                         this.parent =  {
16877                              el : new Roo.bootstrap.layout.Border({
16878                                  el : document.body, 
16879                      
16880                                  center: {
16881                                     titlebar: false,
16882                                     autoScroll:false,
16883                                     closeOnTab: true,
16884                                     tabPosition: 'top',
16885                                       //resizeTabs: true,
16886                                     alwaysShowTabs: true,
16887                                     hideTabs: false
16888                                      //minTabWidth: 140
16889                                  }
16890                              })
16891                         
16892                          };
16893                          break;
16894                     }
16895                          
16896                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16897                         this.parent = { el :  new  Roo.bootstrap.Body() };
16898                         Roo.debug && Roo.log("setting el to doc body");
16899                          
16900                     } else {
16901                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16902                     }
16903                     break;
16904                 case 'bootstrap':
16905                     this.parent = { el : true};
16906                     // fall through
16907                 default:
16908                     el = Roo.get(ename);
16909                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16910                         this.parent = { el : true};
16911                     }
16912                     
16913                     break;
16914             }
16915                 
16916             
16917             if (!el && !this.parent) {
16918                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16919                 return;
16920             }
16921         }
16922         
16923         Roo.debug && Roo.log("EL:");
16924         Roo.debug && Roo.log(el);
16925         Roo.debug && Roo.log("this.parent.el:");
16926         Roo.debug && Roo.log(this.parent.el);
16927         
16928
16929         // altertive root elements ??? - we need a better way to indicate these.
16930         var is_alt = Roo.XComponent.is_alt ||
16931                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16932                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16933                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16934         
16935         
16936         
16937         if (!this.parent && is_alt) {
16938             //el = Roo.get(document.body);
16939             this.parent = { el : true };
16940         }
16941             
16942             
16943         
16944         if (!this.parent) {
16945             
16946             Roo.debug && Roo.log("no parent - creating one");
16947             
16948             el = el ? Roo.get(el) : false;      
16949             
16950             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16951                 
16952                 this.parent =  {
16953                     el : new Roo.bootstrap.layout.Border({
16954                         el: el || document.body,
16955                     
16956                         center: {
16957                             titlebar: false,
16958                             autoScroll:false,
16959                             closeOnTab: true,
16960                             tabPosition: 'top',
16961                              //resizeTabs: true,
16962                             alwaysShowTabs: false,
16963                             hideTabs: true,
16964                             minTabWidth: 140,
16965                             overflow: 'visible'
16966                          }
16967                      })
16968                 };
16969             } else {
16970             
16971                 // it's a top level one..
16972                 this.parent =  {
16973                     el : new Roo.BorderLayout(el || document.body, {
16974                         center: {
16975                             titlebar: false,
16976                             autoScroll:false,
16977                             closeOnTab: true,
16978                             tabPosition: 'top',
16979                              //resizeTabs: true,
16980                             alwaysShowTabs: el && hp? false :  true,
16981                             hideTabs: el || !hp ? true :  false,
16982                             minTabWidth: 140
16983                          }
16984                     })
16985                 };
16986             }
16987         }
16988         
16989         if (!this.parent.el) {
16990                 // probably an old style ctor, which has been disabled.
16991                 return;
16992
16993         }
16994                 // The 'tree' method is  '_tree now' 
16995             
16996         tree.region = tree.region || this.region;
16997         var is_body = false;
16998         if (this.parent.el === true) {
16999             // bootstrap... - body..
17000             if (el) {
17001                 tree.el = el;
17002             }
17003             this.parent.el = Roo.factory(tree);
17004             is_body = true;
17005         }
17006         
17007         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17008         this.fireEvent('built', this);
17009         
17010         this.panel = this.el;
17011         this.layout = this.panel.layout;
17012         this.parentLayout = this.parent.layout  || false;  
17013          
17014     }
17015     
17016 });
17017
17018 Roo.apply(Roo.XComponent, {
17019     /**
17020      * @property  hideProgress
17021      * true to disable the building progress bar.. usefull on single page renders.
17022      * @type Boolean
17023      */
17024     hideProgress : false,
17025     /**
17026      * @property  buildCompleted
17027      * True when the builder has completed building the interface.
17028      * @type Boolean
17029      */
17030     buildCompleted : false,
17031      
17032     /**
17033      * @property  topModule
17034      * the upper most module - uses document.element as it's constructor.
17035      * @type Object
17036      */
17037      
17038     topModule  : false,
17039       
17040     /**
17041      * @property  modules
17042      * array of modules to be created by registration system.
17043      * @type {Array} of Roo.XComponent
17044      */
17045     
17046     modules : [],
17047     /**
17048      * @property  elmodules
17049      * array of modules to be created by which use #ID 
17050      * @type {Array} of Roo.XComponent
17051      */
17052      
17053     elmodules : [],
17054
17055      /**
17056      * @property  is_alt
17057      * Is an alternative Root - normally used by bootstrap or other systems,
17058      *    where the top element in the tree can wrap 'body' 
17059      * @type {boolean}  (default false)
17060      */
17061      
17062     is_alt : false,
17063     /**
17064      * @property  build_from_html
17065      * Build elements from html - used by bootstrap HTML stuff 
17066      *    - this is cleared after build is completed
17067      * @type {boolean}    (default false)
17068      */
17069      
17070     build_from_html : false,
17071     /**
17072      * Register components to be built later.
17073      *
17074      * This solves the following issues
17075      * - Building is not done on page load, but after an authentication process has occured.
17076      * - Interface elements are registered on page load
17077      * - Parent Interface elements may not be loaded before child, so this handles that..
17078      * 
17079      *
17080      * example:
17081      * 
17082      * MyApp.register({
17083           order : '000001',
17084           module : 'Pman.Tab.projectMgr',
17085           region : 'center',
17086           parent : 'Pman.layout',
17087           disabled : false,  // or use a function..
17088         })
17089      
17090      * * @param {Object} details about module
17091      */
17092     register : function(obj) {
17093                 
17094         Roo.XComponent.event.fireEvent('register', obj);
17095         switch(typeof(obj.disabled) ) {
17096                 
17097             case 'undefined':
17098                 break;
17099             
17100             case 'function':
17101                 if ( obj.disabled() ) {
17102                         return;
17103                 }
17104                 break;
17105             
17106             default:
17107                 if (obj.disabled || obj.region == '#disabled') {
17108                         return;
17109                 }
17110                 break;
17111         }
17112                 
17113         this.modules.push(obj);
17114          
17115     },
17116     /**
17117      * convert a string to an object..
17118      * eg. 'AAA.BBB' -> finds AAA.BBB
17119
17120      */
17121     
17122     toObject : function(str)
17123     {
17124         if (!str || typeof(str) == 'object') {
17125             return str;
17126         }
17127         if (str.substring(0,1) == '#') {
17128             return str;
17129         }
17130
17131         var ar = str.split('.');
17132         var rt, o;
17133         rt = ar.shift();
17134             /** eval:var:o */
17135         try {
17136             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17137         } catch (e) {
17138             throw "Module not found : " + str;
17139         }
17140         
17141         if (o === false) {
17142             throw "Module not found : " + str;
17143         }
17144         Roo.each(ar, function(e) {
17145             if (typeof(o[e]) == 'undefined') {
17146                 throw "Module not found : " + str;
17147             }
17148             o = o[e];
17149         });
17150         
17151         return o;
17152         
17153     },
17154     
17155     
17156     /**
17157      * move modules into their correct place in the tree..
17158      * 
17159      */
17160     preBuild : function ()
17161     {
17162         var _t = this;
17163         Roo.each(this.modules , function (obj)
17164         {
17165             Roo.XComponent.event.fireEvent('beforebuild', obj);
17166             
17167             var opar = obj.parent;
17168             try { 
17169                 obj.parent = this.toObject(opar);
17170             } catch(e) {
17171                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17172                 return;
17173             }
17174             
17175             if (!obj.parent) {
17176                 Roo.debug && Roo.log("GOT top level module");
17177                 Roo.debug && Roo.log(obj);
17178                 obj.modules = new Roo.util.MixedCollection(false, 
17179                     function(o) { return o.order + '' }
17180                 );
17181                 this.topModule = obj;
17182                 return;
17183             }
17184                         // parent is a string (usually a dom element name..)
17185             if (typeof(obj.parent) == 'string') {
17186                 this.elmodules.push(obj);
17187                 return;
17188             }
17189             if (obj.parent.constructor != Roo.XComponent) {
17190                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17191             }
17192             if (!obj.parent.modules) {
17193                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17194                     function(o) { return o.order + '' }
17195                 );
17196             }
17197             if (obj.parent.disabled) {
17198                 obj.disabled = true;
17199             }
17200             obj.parent.modules.add(obj);
17201         }, this);
17202     },
17203     
17204      /**
17205      * make a list of modules to build.
17206      * @return {Array} list of modules. 
17207      */ 
17208     
17209     buildOrder : function()
17210     {
17211         var _this = this;
17212         var cmp = function(a,b) {   
17213             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17214         };
17215         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17216             throw "No top level modules to build";
17217         }
17218         
17219         // make a flat list in order of modules to build.
17220         var mods = this.topModule ? [ this.topModule ] : [];
17221                 
17222         
17223         // elmodules (is a list of DOM based modules )
17224         Roo.each(this.elmodules, function(e) {
17225             mods.push(e);
17226             if (!this.topModule &&
17227                 typeof(e.parent) == 'string' &&
17228                 e.parent.substring(0,1) == '#' &&
17229                 Roo.get(e.parent.substr(1))
17230                ) {
17231                 
17232                 _this.topModule = e;
17233             }
17234             
17235         });
17236
17237         
17238         // add modules to their parents..
17239         var addMod = function(m) {
17240             Roo.debug && Roo.log("build Order: add: " + m.name);
17241                 
17242             mods.push(m);
17243             if (m.modules && !m.disabled) {
17244                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17245                 m.modules.keySort('ASC',  cmp );
17246                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17247     
17248                 m.modules.each(addMod);
17249             } else {
17250                 Roo.debug && Roo.log("build Order: no child modules");
17251             }
17252             // not sure if this is used any more..
17253             if (m.finalize) {
17254                 m.finalize.name = m.name + " (clean up) ";
17255                 mods.push(m.finalize);
17256             }
17257             
17258         }
17259         if (this.topModule && this.topModule.modules) { 
17260             this.topModule.modules.keySort('ASC',  cmp );
17261             this.topModule.modules.each(addMod);
17262         } 
17263         return mods;
17264     },
17265     
17266      /**
17267      * Build the registered modules.
17268      * @param {Object} parent element.
17269      * @param {Function} optional method to call after module has been added.
17270      * 
17271      */ 
17272    
17273     build : function(opts) 
17274     {
17275         
17276         if (typeof(opts) != 'undefined') {
17277             Roo.apply(this,opts);
17278         }
17279         
17280         this.preBuild();
17281         var mods = this.buildOrder();
17282       
17283         //this.allmods = mods;
17284         //Roo.debug && Roo.log(mods);
17285         //return;
17286         if (!mods.length) { // should not happen
17287             throw "NO modules!!!";
17288         }
17289         
17290         
17291         var msg = "Building Interface...";
17292         // flash it up as modal - so we store the mask!?
17293         if (!this.hideProgress && Roo.MessageBox) {
17294             Roo.MessageBox.show({ title: 'loading' });
17295             Roo.MessageBox.show({
17296                title: "Please wait...",
17297                msg: msg,
17298                width:450,
17299                progress:true,
17300                buttons : false,
17301                closable:false,
17302                modal: false
17303               
17304             });
17305         }
17306         var total = mods.length;
17307         
17308         var _this = this;
17309         var progressRun = function() {
17310             if (!mods.length) {
17311                 Roo.debug && Roo.log('hide?');
17312                 if (!this.hideProgress && Roo.MessageBox) {
17313                     Roo.MessageBox.hide();
17314                 }
17315                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17316                 
17317                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17318                 
17319                 // THE END...
17320                 return false;   
17321             }
17322             
17323             var m = mods.shift();
17324             
17325             
17326             Roo.debug && Roo.log(m);
17327             // not sure if this is supported any more.. - modules that are are just function
17328             if (typeof(m) == 'function') { 
17329                 m.call(this);
17330                 return progressRun.defer(10, _this);
17331             } 
17332             
17333             
17334             msg = "Building Interface " + (total  - mods.length) + 
17335                     " of " + total + 
17336                     (m.name ? (' - ' + m.name) : '');
17337                         Roo.debug && Roo.log(msg);
17338             if (!_this.hideProgress &&  Roo.MessageBox) { 
17339                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17340             }
17341             
17342          
17343             // is the module disabled?
17344             var disabled = (typeof(m.disabled) == 'function') ?
17345                 m.disabled.call(m.module.disabled) : m.disabled;    
17346             
17347             
17348             if (disabled) {
17349                 return progressRun(); // we do not update the display!
17350             }
17351             
17352             // now build 
17353             
17354                         
17355                         
17356             m.render();
17357             // it's 10 on top level, and 1 on others??? why...
17358             return progressRun.defer(10, _this);
17359              
17360         }
17361         progressRun.defer(1, _this);
17362      
17363         
17364         
17365     },
17366     /**
17367      * Overlay a set of modified strings onto a component
17368      * This is dependant on our builder exporting the strings and 'named strings' elements.
17369      * 
17370      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17371      * @param {Object} associative array of 'named' string and it's new value.
17372      * 
17373      */
17374         overlayStrings : function( component, strings )
17375     {
17376         if (typeof(component['_named_strings']) == 'undefined') {
17377             throw "ERROR: component does not have _named_strings";
17378         }
17379         for ( var k in strings ) {
17380             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17381             if (md !== false) {
17382                 component['_strings'][md] = strings[k];
17383             } else {
17384                 Roo.log('could not find named string: ' + k + ' in');
17385                 Roo.log(component);
17386             }
17387             
17388         }
17389         
17390     },
17391     
17392         
17393         /**
17394          * Event Object.
17395          *
17396          *
17397          */
17398         event: false, 
17399     /**
17400          * wrapper for event.on - aliased later..  
17401          * Typically use to register a event handler for register:
17402          *
17403          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17404          *
17405          */
17406     on : false
17407    
17408     
17409     
17410 });
17411
17412 Roo.XComponent.event = new Roo.util.Observable({
17413                 events : { 
17414                         /**
17415                          * @event register
17416                          * Fires when an Component is registered,
17417                          * set the disable property on the Component to stop registration.
17418                          * @param {Roo.XComponent} c the component being registerd.
17419                          * 
17420                          */
17421                         'register' : true,
17422             /**
17423                          * @event beforebuild
17424                          * Fires before each Component is built
17425                          * can be used to apply permissions.
17426                          * @param {Roo.XComponent} c the component being registerd.
17427                          * 
17428                          */
17429                         'beforebuild' : true,
17430                         /**
17431                          * @event buildcomplete
17432                          * Fires on the top level element when all elements have been built
17433                          * @param {Roo.XComponent} the top level component.
17434                          */
17435                         'buildcomplete' : true
17436                         
17437                 }
17438 });
17439
17440 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17441  //
17442  /**
17443  * marked - a markdown parser
17444  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17445  * https://github.com/chjj/marked
17446  */
17447
17448
17449 /**
17450  *
17451  * Roo.Markdown - is a very crude wrapper around marked..
17452  *
17453  * usage:
17454  * 
17455  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17456  * 
17457  * Note: move the sample code to the bottom of this
17458  * file before uncommenting it.
17459  *
17460  */
17461
17462 Roo.Markdown = {};
17463 Roo.Markdown.toHtml = function(text) {
17464     
17465     var c = new Roo.Markdown.marked.setOptions({
17466             renderer: new Roo.Markdown.marked.Renderer(),
17467             gfm: true,
17468             tables: true,
17469             breaks: false,
17470             pedantic: false,
17471             sanitize: false,
17472             smartLists: true,
17473             smartypants: false
17474           });
17475     // A FEW HACKS!!?
17476     
17477     text = text.replace(/\\\n/g,' ');
17478     return Roo.Markdown.marked(text);
17479 };
17480 //
17481 // converter
17482 //
17483 // Wraps all "globals" so that the only thing
17484 // exposed is makeHtml().
17485 //
17486 (function() {
17487     
17488      /**
17489          * eval:var:escape
17490          * eval:var:unescape
17491          * eval:var:replace
17492          */
17493       
17494     /**
17495      * Helpers
17496      */
17497     
17498     var escape = function (html, encode) {
17499       return html
17500         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17501         .replace(/</g, '&lt;')
17502         .replace(/>/g, '&gt;')
17503         .replace(/"/g, '&quot;')
17504         .replace(/'/g, '&#39;');
17505     }
17506     
17507     var unescape = function (html) {
17508         // explicitly match decimal, hex, and named HTML entities 
17509       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17510         n = n.toLowerCase();
17511         if (n === 'colon') { return ':'; }
17512         if (n.charAt(0) === '#') {
17513           return n.charAt(1) === 'x'
17514             ? String.fromCharCode(parseInt(n.substring(2), 16))
17515             : String.fromCharCode(+n.substring(1));
17516         }
17517         return '';
17518       });
17519     }
17520     
17521     var replace = function (regex, opt) {
17522       regex = regex.source;
17523       opt = opt || '';
17524       return function self(name, val) {
17525         if (!name) { return new RegExp(regex, opt); }
17526         val = val.source || val;
17527         val = val.replace(/(^|[^\[])\^/g, '$1');
17528         regex = regex.replace(name, val);
17529         return self;
17530       };
17531     }
17532
17533
17534          /**
17535          * eval:var:noop
17536     */
17537     var noop = function () {}
17538     noop.exec = noop;
17539     
17540          /**
17541          * eval:var:merge
17542     */
17543     var merge = function (obj) {
17544       var i = 1
17545         , target
17546         , key;
17547     
17548       for (; i < arguments.length; i++) {
17549         target = arguments[i];
17550         for (key in target) {
17551           if (Object.prototype.hasOwnProperty.call(target, key)) {
17552             obj[key] = target[key];
17553           }
17554         }
17555       }
17556     
17557       return obj;
17558     }
17559     
17560     
17561     /**
17562      * Block-Level Grammar
17563      */
17564     
17565     
17566     
17567     
17568     var block = {
17569       newline: /^\n+/,
17570       code: /^( {4}[^\n]+\n*)+/,
17571       fences: noop,
17572       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17573       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17574       nptable: noop,
17575       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17576       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17577       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17578       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17579       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17580       table: noop,
17581       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17582       text: /^[^\n]+/
17583     };
17584     
17585     block.bullet = /(?:[*+-]|\d+\.)/;
17586     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17587     block.item = replace(block.item, 'gm')
17588       (/bull/g, block.bullet)
17589       ();
17590     
17591     block.list = replace(block.list)
17592       (/bull/g, block.bullet)
17593       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17594       ('def', '\\n+(?=' + block.def.source + ')')
17595       ();
17596     
17597     block.blockquote = replace(block.blockquote)
17598       ('def', block.def)
17599       ();
17600     
17601     block._tag = '(?!(?:'
17602       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17603       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17604       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17605     
17606     block.html = replace(block.html)
17607       ('comment', /<!--[\s\S]*?-->/)
17608       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17609       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17610       (/tag/g, block._tag)
17611       ();
17612     
17613     block.paragraph = replace(block.paragraph)
17614       ('hr', block.hr)
17615       ('heading', block.heading)
17616       ('lheading', block.lheading)
17617       ('blockquote', block.blockquote)
17618       ('tag', '<' + block._tag)
17619       ('def', block.def)
17620       ();
17621     
17622     /**
17623      * Normal Block Grammar
17624      */
17625     
17626     block.normal = merge({}, block);
17627     
17628     /**
17629      * GFM Block Grammar
17630      */
17631     
17632     block.gfm = merge({}, block.normal, {
17633       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17634       paragraph: /^/,
17635       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17636     });
17637     
17638     block.gfm.paragraph = replace(block.paragraph)
17639       ('(?!', '(?!'
17640         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17641         + block.list.source.replace('\\1', '\\3') + '|')
17642       ();
17643     
17644     /**
17645      * GFM + Tables Block Grammar
17646      */
17647     
17648     block.tables = merge({}, block.gfm, {
17649       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17650       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17651     });
17652     
17653     /**
17654      * Block Lexer
17655      */
17656     
17657     var Lexer = function (options) {
17658       this.tokens = [];
17659       this.tokens.links = {};
17660       this.options = options || marked.defaults;
17661       this.rules = block.normal;
17662     
17663       if (this.options.gfm) {
17664         if (this.options.tables) {
17665           this.rules = block.tables;
17666         } else {
17667           this.rules = block.gfm;
17668         }
17669       }
17670     }
17671     
17672     /**
17673      * Expose Block Rules
17674      */
17675     
17676     Lexer.rules = block;
17677     
17678     /**
17679      * Static Lex Method
17680      */
17681     
17682     Lexer.lex = function(src, options) {
17683       var lexer = new Lexer(options);
17684       return lexer.lex(src);
17685     };
17686     
17687     /**
17688      * Preprocessing
17689      */
17690     
17691     Lexer.prototype.lex = function(src) {
17692       src = src
17693         .replace(/\r\n|\r/g, '\n')
17694         .replace(/\t/g, '    ')
17695         .replace(/\u00a0/g, ' ')
17696         .replace(/\u2424/g, '\n');
17697     
17698       return this.token(src, true);
17699     };
17700     
17701     /**
17702      * Lexing
17703      */
17704     
17705     Lexer.prototype.token = function(src, top, bq) {
17706       var src = src.replace(/^ +$/gm, '')
17707         , next
17708         , loose
17709         , cap
17710         , bull
17711         , b
17712         , item
17713         , space
17714         , i
17715         , l;
17716     
17717       while (src) {
17718         // newline
17719         if (cap = this.rules.newline.exec(src)) {
17720           src = src.substring(cap[0].length);
17721           if (cap[0].length > 1) {
17722             this.tokens.push({
17723               type: 'space'
17724             });
17725           }
17726         }
17727     
17728         // code
17729         if (cap = this.rules.code.exec(src)) {
17730           src = src.substring(cap[0].length);
17731           cap = cap[0].replace(/^ {4}/gm, '');
17732           this.tokens.push({
17733             type: 'code',
17734             text: !this.options.pedantic
17735               ? cap.replace(/\n+$/, '')
17736               : cap
17737           });
17738           continue;
17739         }
17740     
17741         // fences (gfm)
17742         if (cap = this.rules.fences.exec(src)) {
17743           src = src.substring(cap[0].length);
17744           this.tokens.push({
17745             type: 'code',
17746             lang: cap[2],
17747             text: cap[3] || ''
17748           });
17749           continue;
17750         }
17751     
17752         // heading
17753         if (cap = this.rules.heading.exec(src)) {
17754           src = src.substring(cap[0].length);
17755           this.tokens.push({
17756             type: 'heading',
17757             depth: cap[1].length,
17758             text: cap[2]
17759           });
17760           continue;
17761         }
17762     
17763         // table no leading pipe (gfm)
17764         if (top && (cap = this.rules.nptable.exec(src))) {
17765           src = src.substring(cap[0].length);
17766     
17767           item = {
17768             type: 'table',
17769             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17770             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17771             cells: cap[3].replace(/\n$/, '').split('\n')
17772           };
17773     
17774           for (i = 0; i < item.align.length; i++) {
17775             if (/^ *-+: *$/.test(item.align[i])) {
17776               item.align[i] = 'right';
17777             } else if (/^ *:-+: *$/.test(item.align[i])) {
17778               item.align[i] = 'center';
17779             } else if (/^ *:-+ *$/.test(item.align[i])) {
17780               item.align[i] = 'left';
17781             } else {
17782               item.align[i] = null;
17783             }
17784           }
17785     
17786           for (i = 0; i < item.cells.length; i++) {
17787             item.cells[i] = item.cells[i].split(/ *\| */);
17788           }
17789     
17790           this.tokens.push(item);
17791     
17792           continue;
17793         }
17794     
17795         // lheading
17796         if (cap = this.rules.lheading.exec(src)) {
17797           src = src.substring(cap[0].length);
17798           this.tokens.push({
17799             type: 'heading',
17800             depth: cap[2] === '=' ? 1 : 2,
17801             text: cap[1]
17802           });
17803           continue;
17804         }
17805     
17806         // hr
17807         if (cap = this.rules.hr.exec(src)) {
17808           src = src.substring(cap[0].length);
17809           this.tokens.push({
17810             type: 'hr'
17811           });
17812           continue;
17813         }
17814     
17815         // blockquote
17816         if (cap = this.rules.blockquote.exec(src)) {
17817           src = src.substring(cap[0].length);
17818     
17819           this.tokens.push({
17820             type: 'blockquote_start'
17821           });
17822     
17823           cap = cap[0].replace(/^ *> ?/gm, '');
17824     
17825           // Pass `top` to keep the current
17826           // "toplevel" state. This is exactly
17827           // how markdown.pl works.
17828           this.token(cap, top, true);
17829     
17830           this.tokens.push({
17831             type: 'blockquote_end'
17832           });
17833     
17834           continue;
17835         }
17836     
17837         // list
17838         if (cap = this.rules.list.exec(src)) {
17839           src = src.substring(cap[0].length);
17840           bull = cap[2];
17841     
17842           this.tokens.push({
17843             type: 'list_start',
17844             ordered: bull.length > 1
17845           });
17846     
17847           // Get each top-level item.
17848           cap = cap[0].match(this.rules.item);
17849     
17850           next = false;
17851           l = cap.length;
17852           i = 0;
17853     
17854           for (; i < l; i++) {
17855             item = cap[i];
17856     
17857             // Remove the list item's bullet
17858             // so it is seen as the next token.
17859             space = item.length;
17860             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17861     
17862             // Outdent whatever the
17863             // list item contains. Hacky.
17864             if (~item.indexOf('\n ')) {
17865               space -= item.length;
17866               item = !this.options.pedantic
17867                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17868                 : item.replace(/^ {1,4}/gm, '');
17869             }
17870     
17871             // Determine whether the next list item belongs here.
17872             // Backpedal if it does not belong in this list.
17873             if (this.options.smartLists && i !== l - 1) {
17874               b = block.bullet.exec(cap[i + 1])[0];
17875               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17876                 src = cap.slice(i + 1).join('\n') + src;
17877                 i = l - 1;
17878               }
17879             }
17880     
17881             // Determine whether item is loose or not.
17882             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17883             // for discount behavior.
17884             loose = next || /\n\n(?!\s*$)/.test(item);
17885             if (i !== l - 1) {
17886               next = item.charAt(item.length - 1) === '\n';
17887               if (!loose) { loose = next; }
17888             }
17889     
17890             this.tokens.push({
17891               type: loose
17892                 ? 'loose_item_start'
17893                 : 'list_item_start'
17894             });
17895     
17896             // Recurse.
17897             this.token(item, false, bq);
17898     
17899             this.tokens.push({
17900               type: 'list_item_end'
17901             });
17902           }
17903     
17904           this.tokens.push({
17905             type: 'list_end'
17906           });
17907     
17908           continue;
17909         }
17910     
17911         // html
17912         if (cap = this.rules.html.exec(src)) {
17913           src = src.substring(cap[0].length);
17914           this.tokens.push({
17915             type: this.options.sanitize
17916               ? 'paragraph'
17917               : 'html',
17918             pre: !this.options.sanitizer
17919               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17920             text: cap[0]
17921           });
17922           continue;
17923         }
17924     
17925         // def
17926         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17927           src = src.substring(cap[0].length);
17928           this.tokens.links[cap[1].toLowerCase()] = {
17929             href: cap[2],
17930             title: cap[3]
17931           };
17932           continue;
17933         }
17934     
17935         // table (gfm)
17936         if (top && (cap = this.rules.table.exec(src))) {
17937           src = src.substring(cap[0].length);
17938     
17939           item = {
17940             type: 'table',
17941             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17942             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17943             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17944           };
17945     
17946           for (i = 0; i < item.align.length; i++) {
17947             if (/^ *-+: *$/.test(item.align[i])) {
17948               item.align[i] = 'right';
17949             } else if (/^ *:-+: *$/.test(item.align[i])) {
17950               item.align[i] = 'center';
17951             } else if (/^ *:-+ *$/.test(item.align[i])) {
17952               item.align[i] = 'left';
17953             } else {
17954               item.align[i] = null;
17955             }
17956           }
17957     
17958           for (i = 0; i < item.cells.length; i++) {
17959             item.cells[i] = item.cells[i]
17960               .replace(/^ *\| *| *\| *$/g, '')
17961               .split(/ *\| */);
17962           }
17963     
17964           this.tokens.push(item);
17965     
17966           continue;
17967         }
17968     
17969         // top-level paragraph
17970         if (top && (cap = this.rules.paragraph.exec(src))) {
17971           src = src.substring(cap[0].length);
17972           this.tokens.push({
17973             type: 'paragraph',
17974             text: cap[1].charAt(cap[1].length - 1) === '\n'
17975               ? cap[1].slice(0, -1)
17976               : cap[1]
17977           });
17978           continue;
17979         }
17980     
17981         // text
17982         if (cap = this.rules.text.exec(src)) {
17983           // Top-level should never reach here.
17984           src = src.substring(cap[0].length);
17985           this.tokens.push({
17986             type: 'text',
17987             text: cap[0]
17988           });
17989           continue;
17990         }
17991     
17992         if (src) {
17993           throw new
17994             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17995         }
17996       }
17997     
17998       return this.tokens;
17999     };
18000     
18001     /**
18002      * Inline-Level Grammar
18003      */
18004     
18005     var inline = {
18006       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18007       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18008       url: noop,
18009       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18010       link: /^!?\[(inside)\]\(href\)/,
18011       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18012       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18013       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18014       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18015       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18016       br: /^ {2,}\n(?!\s*$)/,
18017       del: noop,
18018       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18019     };
18020     
18021     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18022     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18023     
18024     inline.link = replace(inline.link)
18025       ('inside', inline._inside)
18026       ('href', inline._href)
18027       ();
18028     
18029     inline.reflink = replace(inline.reflink)
18030       ('inside', inline._inside)
18031       ();
18032     
18033     /**
18034      * Normal Inline Grammar
18035      */
18036     
18037     inline.normal = merge({}, inline);
18038     
18039     /**
18040      * Pedantic Inline Grammar
18041      */
18042     
18043     inline.pedantic = merge({}, inline.normal, {
18044       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18045       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18046     });
18047     
18048     /**
18049      * GFM Inline Grammar
18050      */
18051     
18052     inline.gfm = merge({}, inline.normal, {
18053       escape: replace(inline.escape)('])', '~|])')(),
18054       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18055       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18056       text: replace(inline.text)
18057         (']|', '~]|')
18058         ('|', '|https?://|')
18059         ()
18060     });
18061     
18062     /**
18063      * GFM + Line Breaks Inline Grammar
18064      */
18065     
18066     inline.breaks = merge({}, inline.gfm, {
18067       br: replace(inline.br)('{2,}', '*')(),
18068       text: replace(inline.gfm.text)('{2,}', '*')()
18069     });
18070     
18071     /**
18072      * Inline Lexer & Compiler
18073      */
18074     
18075     var InlineLexer  = function (links, options) {
18076       this.options = options || marked.defaults;
18077       this.links = links;
18078       this.rules = inline.normal;
18079       this.renderer = this.options.renderer || new Renderer;
18080       this.renderer.options = this.options;
18081     
18082       if (!this.links) {
18083         throw new
18084           Error('Tokens array requires a `links` property.');
18085       }
18086     
18087       if (this.options.gfm) {
18088         if (this.options.breaks) {
18089           this.rules = inline.breaks;
18090         } else {
18091           this.rules = inline.gfm;
18092         }
18093       } else if (this.options.pedantic) {
18094         this.rules = inline.pedantic;
18095       }
18096     }
18097     
18098     /**
18099      * Expose Inline Rules
18100      */
18101     
18102     InlineLexer.rules = inline;
18103     
18104     /**
18105      * Static Lexing/Compiling Method
18106      */
18107     
18108     InlineLexer.output = function(src, links, options) {
18109       var inline = new InlineLexer(links, options);
18110       return inline.output(src);
18111     };
18112     
18113     /**
18114      * Lexing/Compiling
18115      */
18116     
18117     InlineLexer.prototype.output = function(src) {
18118       var out = ''
18119         , link
18120         , text
18121         , href
18122         , cap;
18123     
18124       while (src) {
18125         // escape
18126         if (cap = this.rules.escape.exec(src)) {
18127           src = src.substring(cap[0].length);
18128           out += cap[1];
18129           continue;
18130         }
18131     
18132         // autolink
18133         if (cap = this.rules.autolink.exec(src)) {
18134           src = src.substring(cap[0].length);
18135           if (cap[2] === '@') {
18136             text = cap[1].charAt(6) === ':'
18137               ? this.mangle(cap[1].substring(7))
18138               : this.mangle(cap[1]);
18139             href = this.mangle('mailto:') + text;
18140           } else {
18141             text = escape(cap[1]);
18142             href = text;
18143           }
18144           out += this.renderer.link(href, null, text);
18145           continue;
18146         }
18147     
18148         // url (gfm)
18149         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18150           src = src.substring(cap[0].length);
18151           text = escape(cap[1]);
18152           href = text;
18153           out += this.renderer.link(href, null, text);
18154           continue;
18155         }
18156     
18157         // tag
18158         if (cap = this.rules.tag.exec(src)) {
18159           if (!this.inLink && /^<a /i.test(cap[0])) {
18160             this.inLink = true;
18161           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18162             this.inLink = false;
18163           }
18164           src = src.substring(cap[0].length);
18165           out += this.options.sanitize
18166             ? this.options.sanitizer
18167               ? this.options.sanitizer(cap[0])
18168               : escape(cap[0])
18169             : cap[0];
18170           continue;
18171         }
18172     
18173         // link
18174         if (cap = this.rules.link.exec(src)) {
18175           src = src.substring(cap[0].length);
18176           this.inLink = true;
18177           out += this.outputLink(cap, {
18178             href: cap[2],
18179             title: cap[3]
18180           });
18181           this.inLink = false;
18182           continue;
18183         }
18184     
18185         // reflink, nolink
18186         if ((cap = this.rules.reflink.exec(src))
18187             || (cap = this.rules.nolink.exec(src))) {
18188           src = src.substring(cap[0].length);
18189           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18190           link = this.links[link.toLowerCase()];
18191           if (!link || !link.href) {
18192             out += cap[0].charAt(0);
18193             src = cap[0].substring(1) + src;
18194             continue;
18195           }
18196           this.inLink = true;
18197           out += this.outputLink(cap, link);
18198           this.inLink = false;
18199           continue;
18200         }
18201     
18202         // strong
18203         if (cap = this.rules.strong.exec(src)) {
18204           src = src.substring(cap[0].length);
18205           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18206           continue;
18207         }
18208     
18209         // em
18210         if (cap = this.rules.em.exec(src)) {
18211           src = src.substring(cap[0].length);
18212           out += this.renderer.em(this.output(cap[2] || cap[1]));
18213           continue;
18214         }
18215     
18216         // code
18217         if (cap = this.rules.code.exec(src)) {
18218           src = src.substring(cap[0].length);
18219           out += this.renderer.codespan(escape(cap[2], true));
18220           continue;
18221         }
18222     
18223         // br
18224         if (cap = this.rules.br.exec(src)) {
18225           src = src.substring(cap[0].length);
18226           out += this.renderer.br();
18227           continue;
18228         }
18229     
18230         // del (gfm)
18231         if (cap = this.rules.del.exec(src)) {
18232           src = src.substring(cap[0].length);
18233           out += this.renderer.del(this.output(cap[1]));
18234           continue;
18235         }
18236     
18237         // text
18238         if (cap = this.rules.text.exec(src)) {
18239           src = src.substring(cap[0].length);
18240           out += this.renderer.text(escape(this.smartypants(cap[0])));
18241           continue;
18242         }
18243     
18244         if (src) {
18245           throw new
18246             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18247         }
18248       }
18249     
18250       return out;
18251     };
18252     
18253     /**
18254      * Compile Link
18255      */
18256     
18257     InlineLexer.prototype.outputLink = function(cap, link) {
18258       var href = escape(link.href)
18259         , title = link.title ? escape(link.title) : null;
18260     
18261       return cap[0].charAt(0) !== '!'
18262         ? this.renderer.link(href, title, this.output(cap[1]))
18263         : this.renderer.image(href, title, escape(cap[1]));
18264     };
18265     
18266     /**
18267      * Smartypants Transformations
18268      */
18269     
18270     InlineLexer.prototype.smartypants = function(text) {
18271       if (!this.options.smartypants)  { return text; }
18272       return text
18273         // em-dashes
18274         .replace(/---/g, '\u2014')
18275         // en-dashes
18276         .replace(/--/g, '\u2013')
18277         // opening singles
18278         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18279         // closing singles & apostrophes
18280         .replace(/'/g, '\u2019')
18281         // opening doubles
18282         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18283         // closing doubles
18284         .replace(/"/g, '\u201d')
18285         // ellipses
18286         .replace(/\.{3}/g, '\u2026');
18287     };
18288     
18289     /**
18290      * Mangle Links
18291      */
18292     
18293     InlineLexer.prototype.mangle = function(text) {
18294       if (!this.options.mangle) { return text; }
18295       var out = ''
18296         , l = text.length
18297         , i = 0
18298         , ch;
18299     
18300       for (; i < l; i++) {
18301         ch = text.charCodeAt(i);
18302         if (Math.random() > 0.5) {
18303           ch = 'x' + ch.toString(16);
18304         }
18305         out += '&#' + ch + ';';
18306       }
18307     
18308       return out;
18309     };
18310     
18311     /**
18312      * Renderer
18313      */
18314     
18315      /**
18316          * eval:var:Renderer
18317     */
18318     
18319     var Renderer   = function (options) {
18320       this.options = options || {};
18321     }
18322     
18323     Renderer.prototype.code = function(code, lang, escaped) {
18324       if (this.options.highlight) {
18325         var out = this.options.highlight(code, lang);
18326         if (out != null && out !== code) {
18327           escaped = true;
18328           code = out;
18329         }
18330       } else {
18331             // hack!!! - it's already escapeD?
18332             escaped = true;
18333       }
18334     
18335       if (!lang) {
18336         return '<pre><code>'
18337           + (escaped ? code : escape(code, true))
18338           + '\n</code></pre>';
18339       }
18340     
18341       return '<pre><code class="'
18342         + this.options.langPrefix
18343         + escape(lang, true)
18344         + '">'
18345         + (escaped ? code : escape(code, true))
18346         + '\n</code></pre>\n';
18347     };
18348     
18349     Renderer.prototype.blockquote = function(quote) {
18350       return '<blockquote>\n' + quote + '</blockquote>\n';
18351     };
18352     
18353     Renderer.prototype.html = function(html) {
18354       return html;
18355     };
18356     
18357     Renderer.prototype.heading = function(text, level, raw) {
18358       return '<h'
18359         + level
18360         + ' id="'
18361         + this.options.headerPrefix
18362         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18363         + '">'
18364         + text
18365         + '</h'
18366         + level
18367         + '>\n';
18368     };
18369     
18370     Renderer.prototype.hr = function() {
18371       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18372     };
18373     
18374     Renderer.prototype.list = function(body, ordered) {
18375       var type = ordered ? 'ol' : 'ul';
18376       return '<' + type + '>\n' + body + '</' + type + '>\n';
18377     };
18378     
18379     Renderer.prototype.listitem = function(text) {
18380       return '<li>' + text + '</li>\n';
18381     };
18382     
18383     Renderer.prototype.paragraph = function(text) {
18384       return '<p>' + text + '</p>\n';
18385     };
18386     
18387     Renderer.prototype.table = function(header, body) {
18388       return '<table class="table table-striped">\n'
18389         + '<thead>\n'
18390         + header
18391         + '</thead>\n'
18392         + '<tbody>\n'
18393         + body
18394         + '</tbody>\n'
18395         + '</table>\n';
18396     };
18397     
18398     Renderer.prototype.tablerow = function(content) {
18399       return '<tr>\n' + content + '</tr>\n';
18400     };
18401     
18402     Renderer.prototype.tablecell = function(content, flags) {
18403       var type = flags.header ? 'th' : 'td';
18404       var tag = flags.align
18405         ? '<' + type + ' style="text-align:' + flags.align + '">'
18406         : '<' + type + '>';
18407       return tag + content + '</' + type + '>\n';
18408     };
18409     
18410     // span level renderer
18411     Renderer.prototype.strong = function(text) {
18412       return '<strong>' + text + '</strong>';
18413     };
18414     
18415     Renderer.prototype.em = function(text) {
18416       return '<em>' + text + '</em>';
18417     };
18418     
18419     Renderer.prototype.codespan = function(text) {
18420       return '<code>' + text + '</code>';
18421     };
18422     
18423     Renderer.prototype.br = function() {
18424       return this.options.xhtml ? '<br/>' : '<br>';
18425     };
18426     
18427     Renderer.prototype.del = function(text) {
18428       return '<del>' + text + '</del>';
18429     };
18430     
18431     Renderer.prototype.link = function(href, title, text) {
18432       if (this.options.sanitize) {
18433         try {
18434           var prot = decodeURIComponent(unescape(href))
18435             .replace(/[^\w:]/g, '')
18436             .toLowerCase();
18437         } catch (e) {
18438           return '';
18439         }
18440         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18441           return '';
18442         }
18443       }
18444       var out = '<a href="' + href + '"';
18445       if (title) {
18446         out += ' title="' + title + '"';
18447       }
18448       out += '>' + text + '</a>';
18449       return out;
18450     };
18451     
18452     Renderer.prototype.image = function(href, title, text) {
18453       var out = '<img src="' + href + '" alt="' + text + '"';
18454       if (title) {
18455         out += ' title="' + title + '"';
18456       }
18457       out += this.options.xhtml ? '/>' : '>';
18458       return out;
18459     };
18460     
18461     Renderer.prototype.text = function(text) {
18462       return text;
18463     };
18464     
18465     /**
18466      * Parsing & Compiling
18467      */
18468          /**
18469          * eval:var:Parser
18470     */
18471     
18472     var Parser= function (options) {
18473       this.tokens = [];
18474       this.token = null;
18475       this.options = options || marked.defaults;
18476       this.options.renderer = this.options.renderer || new Renderer;
18477       this.renderer = this.options.renderer;
18478       this.renderer.options = this.options;
18479     }
18480     
18481     /**
18482      * Static Parse Method
18483      */
18484     
18485     Parser.parse = function(src, options, renderer) {
18486       var parser = new Parser(options, renderer);
18487       return parser.parse(src);
18488     };
18489     
18490     /**
18491      * Parse Loop
18492      */
18493     
18494     Parser.prototype.parse = function(src) {
18495       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18496       this.tokens = src.reverse();
18497     
18498       var out = '';
18499       while (this.next()) {
18500         out += this.tok();
18501       }
18502     
18503       return out;
18504     };
18505     
18506     /**
18507      * Next Token
18508      */
18509     
18510     Parser.prototype.next = function() {
18511       return this.token = this.tokens.pop();
18512     };
18513     
18514     /**
18515      * Preview Next Token
18516      */
18517     
18518     Parser.prototype.peek = function() {
18519       return this.tokens[this.tokens.length - 1] || 0;
18520     };
18521     
18522     /**
18523      * Parse Text Tokens
18524      */
18525     
18526     Parser.prototype.parseText = function() {
18527       var body = this.token.text;
18528     
18529       while (this.peek().type === 'text') {
18530         body += '\n' + this.next().text;
18531       }
18532     
18533       return this.inline.output(body);
18534     };
18535     
18536     /**
18537      * Parse Current Token
18538      */
18539     
18540     Parser.prototype.tok = function() {
18541       switch (this.token.type) {
18542         case 'space': {
18543           return '';
18544         }
18545         case 'hr': {
18546           return this.renderer.hr();
18547         }
18548         case 'heading': {
18549           return this.renderer.heading(
18550             this.inline.output(this.token.text),
18551             this.token.depth,
18552             this.token.text);
18553         }
18554         case 'code': {
18555           return this.renderer.code(this.token.text,
18556             this.token.lang,
18557             this.token.escaped);
18558         }
18559         case 'table': {
18560           var header = ''
18561             , body = ''
18562             , i
18563             , row
18564             , cell
18565             , flags
18566             , j;
18567     
18568           // header
18569           cell = '';
18570           for (i = 0; i < this.token.header.length; i++) {
18571             flags = { header: true, align: this.token.align[i] };
18572             cell += this.renderer.tablecell(
18573               this.inline.output(this.token.header[i]),
18574               { header: true, align: this.token.align[i] }
18575             );
18576           }
18577           header += this.renderer.tablerow(cell);
18578     
18579           for (i = 0; i < this.token.cells.length; i++) {
18580             row = this.token.cells[i];
18581     
18582             cell = '';
18583             for (j = 0; j < row.length; j++) {
18584               cell += this.renderer.tablecell(
18585                 this.inline.output(row[j]),
18586                 { header: false, align: this.token.align[j] }
18587               );
18588             }
18589     
18590             body += this.renderer.tablerow(cell);
18591           }
18592           return this.renderer.table(header, body);
18593         }
18594         case 'blockquote_start': {
18595           var body = '';
18596     
18597           while (this.next().type !== 'blockquote_end') {
18598             body += this.tok();
18599           }
18600     
18601           return this.renderer.blockquote(body);
18602         }
18603         case 'list_start': {
18604           var body = ''
18605             , ordered = this.token.ordered;
18606     
18607           while (this.next().type !== 'list_end') {
18608             body += this.tok();
18609           }
18610     
18611           return this.renderer.list(body, ordered);
18612         }
18613         case 'list_item_start': {
18614           var body = '';
18615     
18616           while (this.next().type !== 'list_item_end') {
18617             body += this.token.type === 'text'
18618               ? this.parseText()
18619               : this.tok();
18620           }
18621     
18622           return this.renderer.listitem(body);
18623         }
18624         case 'loose_item_start': {
18625           var body = '';
18626     
18627           while (this.next().type !== 'list_item_end') {
18628             body += this.tok();
18629           }
18630     
18631           return this.renderer.listitem(body);
18632         }
18633         case 'html': {
18634           var html = !this.token.pre && !this.options.pedantic
18635             ? this.inline.output(this.token.text)
18636             : this.token.text;
18637           return this.renderer.html(html);
18638         }
18639         case 'paragraph': {
18640           return this.renderer.paragraph(this.inline.output(this.token.text));
18641         }
18642         case 'text': {
18643           return this.renderer.paragraph(this.parseText());
18644         }
18645       }
18646     };
18647   
18648     
18649     /**
18650      * Marked
18651      */
18652          /**
18653          * eval:var:marked
18654     */
18655     var marked = function (src, opt, callback) {
18656       if (callback || typeof opt === 'function') {
18657         if (!callback) {
18658           callback = opt;
18659           opt = null;
18660         }
18661     
18662         opt = merge({}, marked.defaults, opt || {});
18663     
18664         var highlight = opt.highlight
18665           , tokens
18666           , pending
18667           , i = 0;
18668     
18669         try {
18670           tokens = Lexer.lex(src, opt)
18671         } catch (e) {
18672           return callback(e);
18673         }
18674     
18675         pending = tokens.length;
18676          /**
18677          * eval:var:done
18678     */
18679         var done = function(err) {
18680           if (err) {
18681             opt.highlight = highlight;
18682             return callback(err);
18683           }
18684     
18685           var out;
18686     
18687           try {
18688             out = Parser.parse(tokens, opt);
18689           } catch (e) {
18690             err = e;
18691           }
18692     
18693           opt.highlight = highlight;
18694     
18695           return err
18696             ? callback(err)
18697             : callback(null, out);
18698         };
18699     
18700         if (!highlight || highlight.length < 3) {
18701           return done();
18702         }
18703     
18704         delete opt.highlight;
18705     
18706         if (!pending) { return done(); }
18707     
18708         for (; i < tokens.length; i++) {
18709           (function(token) {
18710             if (token.type !== 'code') {
18711               return --pending || done();
18712             }
18713             return highlight(token.text, token.lang, function(err, code) {
18714               if (err) { return done(err); }
18715               if (code == null || code === token.text) {
18716                 return --pending || done();
18717               }
18718               token.text = code;
18719               token.escaped = true;
18720               --pending || done();
18721             });
18722           })(tokens[i]);
18723         }
18724     
18725         return;
18726       }
18727       try {
18728         if (opt) { opt = merge({}, marked.defaults, opt); }
18729         return Parser.parse(Lexer.lex(src, opt), opt);
18730       } catch (e) {
18731         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18732         if ((opt || marked.defaults).silent) {
18733           return '<p>An error occured:</p><pre>'
18734             + escape(e.message + '', true)
18735             + '</pre>';
18736         }
18737         throw e;
18738       }
18739     }
18740     
18741     /**
18742      * Options
18743      */
18744     
18745     marked.options =
18746     marked.setOptions = function(opt) {
18747       merge(marked.defaults, opt);
18748       return marked;
18749     };
18750     
18751     marked.defaults = {
18752       gfm: true,
18753       tables: true,
18754       breaks: false,
18755       pedantic: false,
18756       sanitize: false,
18757       sanitizer: null,
18758       mangle: true,
18759       smartLists: false,
18760       silent: false,
18761       highlight: null,
18762       langPrefix: 'lang-',
18763       smartypants: false,
18764       headerPrefix: '',
18765       renderer: new Renderer,
18766       xhtml: false
18767     };
18768     
18769     /**
18770      * Expose
18771      */
18772     
18773     marked.Parser = Parser;
18774     marked.parser = Parser.parse;
18775     
18776     marked.Renderer = Renderer;
18777     
18778     marked.Lexer = Lexer;
18779     marked.lexer = Lexer.lex;
18780     
18781     marked.InlineLexer = InlineLexer;
18782     marked.inlineLexer = InlineLexer.output;
18783     
18784     marked.parse = marked;
18785     
18786     Roo.Markdown.marked = marked;
18787
18788 })();/*
18789  * Based on:
18790  * Ext JS Library 1.1.1
18791  * Copyright(c) 2006-2007, Ext JS, LLC.
18792  *
18793  * Originally Released Under LGPL - original licence link has changed is not relivant.
18794  *
18795  * Fork - LGPL
18796  * <script type="text/javascript">
18797  */
18798
18799
18800
18801 /*
18802  * These classes are derivatives of the similarly named classes in the YUI Library.
18803  * The original license:
18804  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18805  * Code licensed under the BSD License:
18806  * http://developer.yahoo.net/yui/license.txt
18807  */
18808
18809 (function() {
18810
18811 var Event=Roo.EventManager;
18812 var Dom=Roo.lib.Dom;
18813
18814 /**
18815  * @class Roo.dd.DragDrop
18816  * @extends Roo.util.Observable
18817  * Defines the interface and base operation of items that that can be
18818  * dragged or can be drop targets.  It was designed to be extended, overriding
18819  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18820  * Up to three html elements can be associated with a DragDrop instance:
18821  * <ul>
18822  * <li>linked element: the element that is passed into the constructor.
18823  * This is the element which defines the boundaries for interaction with
18824  * other DragDrop objects.</li>
18825  * <li>handle element(s): The drag operation only occurs if the element that
18826  * was clicked matches a handle element.  By default this is the linked
18827  * element, but there are times that you will want only a portion of the
18828  * linked element to initiate the drag operation, and the setHandleElId()
18829  * method provides a way to define this.</li>
18830  * <li>drag element: this represents the element that would be moved along
18831  * with the cursor during a drag operation.  By default, this is the linked
18832  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18833  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18834  * </li>
18835  * </ul>
18836  * This class should not be instantiated until the onload event to ensure that
18837  * the associated elements are available.
18838  * The following would define a DragDrop obj that would interact with any
18839  * other DragDrop obj in the "group1" group:
18840  * <pre>
18841  *  dd = new Roo.dd.DragDrop("div1", "group1");
18842  * </pre>
18843  * Since none of the event handlers have been implemented, nothing would
18844  * actually happen if you were to run the code above.  Normally you would
18845  * override this class or one of the default implementations, but you can
18846  * also override the methods you want on an instance of the class...
18847  * <pre>
18848  *  dd.onDragDrop = function(e, id) {
18849  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18850  *  }
18851  * </pre>
18852  * @constructor
18853  * @param {String} id of the element that is linked to this instance
18854  * @param {String} sGroup the group of related DragDrop objects
18855  * @param {object} config an object containing configurable attributes
18856  *                Valid properties for DragDrop:
18857  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18858  */
18859 Roo.dd.DragDrop = function(id, sGroup, config) {
18860     if (id) {
18861         this.init(id, sGroup, config);
18862     }
18863     
18864 };
18865
18866 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18867
18868     /**
18869      * The id of the element associated with this object.  This is what we
18870      * refer to as the "linked element" because the size and position of
18871      * this element is used to determine when the drag and drop objects have
18872      * interacted.
18873      * @property id
18874      * @type String
18875      */
18876     id: null,
18877
18878     /**
18879      * Configuration attributes passed into the constructor
18880      * @property config
18881      * @type object
18882      */
18883     config: null,
18884
18885     /**
18886      * The id of the element that will be dragged.  By default this is same
18887      * as the linked element , but could be changed to another element. Ex:
18888      * Roo.dd.DDProxy
18889      * @property dragElId
18890      * @type String
18891      * @private
18892      */
18893     dragElId: null,
18894
18895     /**
18896      * the id of the element that initiates the drag operation.  By default
18897      * this is the linked element, but could be changed to be a child of this
18898      * element.  This lets us do things like only starting the drag when the
18899      * header element within the linked html element is clicked.
18900      * @property handleElId
18901      * @type String
18902      * @private
18903      */
18904     handleElId: null,
18905
18906     /**
18907      * An associative array of HTML tags that will be ignored if clicked.
18908      * @property invalidHandleTypes
18909      * @type {string: string}
18910      */
18911     invalidHandleTypes: null,
18912
18913     /**
18914      * An associative array of ids for elements that will be ignored if clicked
18915      * @property invalidHandleIds
18916      * @type {string: string}
18917      */
18918     invalidHandleIds: null,
18919
18920     /**
18921      * An indexted array of css class names for elements that will be ignored
18922      * if clicked.
18923      * @property invalidHandleClasses
18924      * @type string[]
18925      */
18926     invalidHandleClasses: null,
18927
18928     /**
18929      * The linked element's absolute X position at the time the drag was
18930      * started
18931      * @property startPageX
18932      * @type int
18933      * @private
18934      */
18935     startPageX: 0,
18936
18937     /**
18938      * The linked element's absolute X position at the time the drag was
18939      * started
18940      * @property startPageY
18941      * @type int
18942      * @private
18943      */
18944     startPageY: 0,
18945
18946     /**
18947      * The group defines a logical collection of DragDrop objects that are
18948      * related.  Instances only get events when interacting with other
18949      * DragDrop object in the same group.  This lets us define multiple
18950      * groups using a single DragDrop subclass if we want.
18951      * @property groups
18952      * @type {string: string}
18953      */
18954     groups: null,
18955
18956     /**
18957      * Individual drag/drop instances can be locked.  This will prevent
18958      * onmousedown start drag.
18959      * @property locked
18960      * @type boolean
18961      * @private
18962      */
18963     locked: false,
18964
18965     /**
18966      * Lock this instance
18967      * @method lock
18968      */
18969     lock: function() { this.locked = true; },
18970
18971     /**
18972      * Unlock this instace
18973      * @method unlock
18974      */
18975     unlock: function() { this.locked = false; },
18976
18977     /**
18978      * By default, all insances can be a drop target.  This can be disabled by
18979      * setting isTarget to false.
18980      * @method isTarget
18981      * @type boolean
18982      */
18983     isTarget: true,
18984
18985     /**
18986      * The padding configured for this drag and drop object for calculating
18987      * the drop zone intersection with this object.
18988      * @method padding
18989      * @type int[]
18990      */
18991     padding: null,
18992
18993     /**
18994      * Cached reference to the linked element
18995      * @property _domRef
18996      * @private
18997      */
18998     _domRef: null,
18999
19000     /**
19001      * Internal typeof flag
19002      * @property __ygDragDrop
19003      * @private
19004      */
19005     __ygDragDrop: true,
19006
19007     /**
19008      * Set to true when horizontal contraints are applied
19009      * @property constrainX
19010      * @type boolean
19011      * @private
19012      */
19013     constrainX: false,
19014
19015     /**
19016      * Set to true when vertical contraints are applied
19017      * @property constrainY
19018      * @type boolean
19019      * @private
19020      */
19021     constrainY: false,
19022
19023     /**
19024      * The left constraint
19025      * @property minX
19026      * @type int
19027      * @private
19028      */
19029     minX: 0,
19030
19031     /**
19032      * The right constraint
19033      * @property maxX
19034      * @type int
19035      * @private
19036      */
19037     maxX: 0,
19038
19039     /**
19040      * The up constraint
19041      * @property minY
19042      * @type int
19043      * @type int
19044      * @private
19045      */
19046     minY: 0,
19047
19048     /**
19049      * The down constraint
19050      * @property maxY
19051      * @type int
19052      * @private
19053      */
19054     maxY: 0,
19055
19056     /**
19057      * Maintain offsets when we resetconstraints.  Set to true when you want
19058      * the position of the element relative to its parent to stay the same
19059      * when the page changes
19060      *
19061      * @property maintainOffset
19062      * @type boolean
19063      */
19064     maintainOffset: false,
19065
19066     /**
19067      * Array of pixel locations the element will snap to if we specified a
19068      * horizontal graduation/interval.  This array is generated automatically
19069      * when you define a tick interval.
19070      * @property xTicks
19071      * @type int[]
19072      */
19073     xTicks: null,
19074
19075     /**
19076      * Array of pixel locations the element will snap to if we specified a
19077      * vertical graduation/interval.  This array is generated automatically
19078      * when you define a tick interval.
19079      * @property yTicks
19080      * @type int[]
19081      */
19082     yTicks: null,
19083
19084     /**
19085      * By default the drag and drop instance will only respond to the primary
19086      * button click (left button for a right-handed mouse).  Set to true to
19087      * allow drag and drop to start with any mouse click that is propogated
19088      * by the browser
19089      * @property primaryButtonOnly
19090      * @type boolean
19091      */
19092     primaryButtonOnly: true,
19093
19094     /**
19095      * The availabe property is false until the linked dom element is accessible.
19096      * @property available
19097      * @type boolean
19098      */
19099     available: false,
19100
19101     /**
19102      * By default, drags can only be initiated if the mousedown occurs in the
19103      * region the linked element is.  This is done in part to work around a
19104      * bug in some browsers that mis-report the mousedown if the previous
19105      * mouseup happened outside of the window.  This property is set to true
19106      * if outer handles are defined.
19107      *
19108      * @property hasOuterHandles
19109      * @type boolean
19110      * @default false
19111      */
19112     hasOuterHandles: false,
19113
19114     /**
19115      * Code that executes immediately before the startDrag event
19116      * @method b4StartDrag
19117      * @private
19118      */
19119     b4StartDrag: function(x, y) { },
19120
19121     /**
19122      * Abstract method called after a drag/drop object is clicked
19123      * and the drag or mousedown time thresholds have beeen met.
19124      * @method startDrag
19125      * @param {int} X click location
19126      * @param {int} Y click location
19127      */
19128     startDrag: function(x, y) { /* override this */ },
19129
19130     /**
19131      * Code that executes immediately before the onDrag event
19132      * @method b4Drag
19133      * @private
19134      */
19135     b4Drag: function(e) { },
19136
19137     /**
19138      * Abstract method called during the onMouseMove event while dragging an
19139      * object.
19140      * @method onDrag
19141      * @param {Event} e the mousemove event
19142      */
19143     onDrag: function(e) { /* override this */ },
19144
19145     /**
19146      * Abstract method called when this element fist begins hovering over
19147      * another DragDrop obj
19148      * @method onDragEnter
19149      * @param {Event} e the mousemove event
19150      * @param {String|DragDrop[]} id In POINT mode, the element
19151      * id this is hovering over.  In INTERSECT mode, an array of one or more
19152      * dragdrop items being hovered over.
19153      */
19154     onDragEnter: function(e, id) { /* override this */ },
19155
19156     /**
19157      * Code that executes immediately before the onDragOver event
19158      * @method b4DragOver
19159      * @private
19160      */
19161     b4DragOver: function(e) { },
19162
19163     /**
19164      * Abstract method called when this element is hovering over another
19165      * DragDrop obj
19166      * @method onDragOver
19167      * @param {Event} e the mousemove event
19168      * @param {String|DragDrop[]} id In POINT mode, the element
19169      * id this is hovering over.  In INTERSECT mode, an array of dd items
19170      * being hovered over.
19171      */
19172     onDragOver: function(e, id) { /* override this */ },
19173
19174     /**
19175      * Code that executes immediately before the onDragOut event
19176      * @method b4DragOut
19177      * @private
19178      */
19179     b4DragOut: function(e) { },
19180
19181     /**
19182      * Abstract method called when we are no longer hovering over an element
19183      * @method onDragOut
19184      * @param {Event} e the mousemove event
19185      * @param {String|DragDrop[]} id In POINT mode, the element
19186      * id this was hovering over.  In INTERSECT mode, an array of dd items
19187      * that the mouse is no longer over.
19188      */
19189     onDragOut: function(e, id) { /* override this */ },
19190
19191     /**
19192      * Code that executes immediately before the onDragDrop event
19193      * @method b4DragDrop
19194      * @private
19195      */
19196     b4DragDrop: function(e) { },
19197
19198     /**
19199      * Abstract method called when this item is dropped on another DragDrop
19200      * obj
19201      * @method onDragDrop
19202      * @param {Event} e the mouseup event
19203      * @param {String|DragDrop[]} id In POINT mode, the element
19204      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19205      * was dropped on.
19206      */
19207     onDragDrop: function(e, id) { /* override this */ },
19208
19209     /**
19210      * Abstract method called when this item is dropped on an area with no
19211      * drop target
19212      * @method onInvalidDrop
19213      * @param {Event} e the mouseup event
19214      */
19215     onInvalidDrop: function(e) { /* override this */ },
19216
19217     /**
19218      * Code that executes immediately before the endDrag event
19219      * @method b4EndDrag
19220      * @private
19221      */
19222     b4EndDrag: function(e) { },
19223
19224     /**
19225      * Fired when we are done dragging the object
19226      * @method endDrag
19227      * @param {Event} e the mouseup event
19228      */
19229     endDrag: function(e) { /* override this */ },
19230
19231     /**
19232      * Code executed immediately before the onMouseDown event
19233      * @method b4MouseDown
19234      * @param {Event} e the mousedown event
19235      * @private
19236      */
19237     b4MouseDown: function(e) {  },
19238
19239     /**
19240      * Event handler that fires when a drag/drop obj gets a mousedown
19241      * @method onMouseDown
19242      * @param {Event} e the mousedown event
19243      */
19244     onMouseDown: function(e) { /* override this */ },
19245
19246     /**
19247      * Event handler that fires when a drag/drop obj gets a mouseup
19248      * @method onMouseUp
19249      * @param {Event} e the mouseup event
19250      */
19251     onMouseUp: function(e) { /* override this */ },
19252
19253     /**
19254      * Override the onAvailable method to do what is needed after the initial
19255      * position was determined.
19256      * @method onAvailable
19257      */
19258     onAvailable: function () {
19259     },
19260
19261     /*
19262      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19263      * @type Object
19264      */
19265     defaultPadding : {left:0, right:0, top:0, bottom:0},
19266
19267     /*
19268      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19269  *
19270  * Usage:
19271  <pre><code>
19272  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19273                 { dragElId: "existingProxyDiv" });
19274  dd.startDrag = function(){
19275      this.constrainTo("parent-id");
19276  };
19277  </code></pre>
19278  * Or you can initalize it using the {@link Roo.Element} object:
19279  <pre><code>
19280  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19281      startDrag : function(){
19282          this.constrainTo("parent-id");
19283      }
19284  });
19285  </code></pre>
19286      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19287      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19288      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19289      * an object containing the sides to pad. For example: {right:10, bottom:10}
19290      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19291      */
19292     constrainTo : function(constrainTo, pad, inContent){
19293         if(typeof pad == "number"){
19294             pad = {left: pad, right:pad, top:pad, bottom:pad};
19295         }
19296         pad = pad || this.defaultPadding;
19297         var b = Roo.get(this.getEl()).getBox();
19298         var ce = Roo.get(constrainTo);
19299         var s = ce.getScroll();
19300         var c, cd = ce.dom;
19301         if(cd == document.body){
19302             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19303         }else{
19304             xy = ce.getXY();
19305             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19306         }
19307
19308
19309         var topSpace = b.y - c.y;
19310         var leftSpace = b.x - c.x;
19311
19312         this.resetConstraints();
19313         this.setXConstraint(leftSpace - (pad.left||0), // left
19314                 c.width - leftSpace - b.width - (pad.right||0) //right
19315         );
19316         this.setYConstraint(topSpace - (pad.top||0), //top
19317                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19318         );
19319     },
19320
19321     /**
19322      * Returns a reference to the linked element
19323      * @method getEl
19324      * @return {HTMLElement} the html element
19325      */
19326     getEl: function() {
19327         if (!this._domRef) {
19328             this._domRef = Roo.getDom(this.id);
19329         }
19330
19331         return this._domRef;
19332     },
19333
19334     /**
19335      * Returns a reference to the actual element to drag.  By default this is
19336      * the same as the html element, but it can be assigned to another
19337      * element. An example of this can be found in Roo.dd.DDProxy
19338      * @method getDragEl
19339      * @return {HTMLElement} the html element
19340      */
19341     getDragEl: function() {
19342         return Roo.getDom(this.dragElId);
19343     },
19344
19345     /**
19346      * Sets up the DragDrop object.  Must be called in the constructor of any
19347      * Roo.dd.DragDrop subclass
19348      * @method init
19349      * @param id the id of the linked element
19350      * @param {String} sGroup the group of related items
19351      * @param {object} config configuration attributes
19352      */
19353     init: function(id, sGroup, config) {
19354         this.initTarget(id, sGroup, config);
19355         if (!Roo.isTouch) {
19356             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19357         }
19358         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19359         // Event.on(this.id, "selectstart", Event.preventDefault);
19360     },
19361
19362     /**
19363      * Initializes Targeting functionality only... the object does not
19364      * get a mousedown handler.
19365      * @method initTarget
19366      * @param id the id of the linked element
19367      * @param {String} sGroup the group of related items
19368      * @param {object} config configuration attributes
19369      */
19370     initTarget: function(id, sGroup, config) {
19371
19372         // configuration attributes
19373         this.config = config || {};
19374
19375         // create a local reference to the drag and drop manager
19376         this.DDM = Roo.dd.DDM;
19377         // initialize the groups array
19378         this.groups = {};
19379
19380         // assume that we have an element reference instead of an id if the
19381         // parameter is not a string
19382         if (typeof id !== "string") {
19383             id = Roo.id(id);
19384         }
19385
19386         // set the id
19387         this.id = id;
19388
19389         // add to an interaction group
19390         this.addToGroup((sGroup) ? sGroup : "default");
19391
19392         // We don't want to register this as the handle with the manager
19393         // so we just set the id rather than calling the setter.
19394         this.handleElId = id;
19395
19396         // the linked element is the element that gets dragged by default
19397         this.setDragElId(id);
19398
19399         // by default, clicked anchors will not start drag operations.
19400         this.invalidHandleTypes = { A: "A" };
19401         this.invalidHandleIds = {};
19402         this.invalidHandleClasses = [];
19403
19404         this.applyConfig();
19405
19406         this.handleOnAvailable();
19407     },
19408
19409     /**
19410      * Applies the configuration parameters that were passed into the constructor.
19411      * This is supposed to happen at each level through the inheritance chain.  So
19412      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19413      * DragDrop in order to get all of the parameters that are available in
19414      * each object.
19415      * @method applyConfig
19416      */
19417     applyConfig: function() {
19418
19419         // configurable properties:
19420         //    padding, isTarget, maintainOffset, primaryButtonOnly
19421         this.padding           = this.config.padding || [0, 0, 0, 0];
19422         this.isTarget          = (this.config.isTarget !== false);
19423         this.maintainOffset    = (this.config.maintainOffset);
19424         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19425
19426     },
19427
19428     /**
19429      * Executed when the linked element is available
19430      * @method handleOnAvailable
19431      * @private
19432      */
19433     handleOnAvailable: function() {
19434         this.available = true;
19435         this.resetConstraints();
19436         this.onAvailable();
19437     },
19438
19439      /**
19440      * Configures the padding for the target zone in px.  Effectively expands
19441      * (or reduces) the virtual object size for targeting calculations.
19442      * Supports css-style shorthand; if only one parameter is passed, all sides
19443      * will have that padding, and if only two are passed, the top and bottom
19444      * will have the first param, the left and right the second.
19445      * @method setPadding
19446      * @param {int} iTop    Top pad
19447      * @param {int} iRight  Right pad
19448      * @param {int} iBot    Bot pad
19449      * @param {int} iLeft   Left pad
19450      */
19451     setPadding: function(iTop, iRight, iBot, iLeft) {
19452         // this.padding = [iLeft, iRight, iTop, iBot];
19453         if (!iRight && 0 !== iRight) {
19454             this.padding = [iTop, iTop, iTop, iTop];
19455         } else if (!iBot && 0 !== iBot) {
19456             this.padding = [iTop, iRight, iTop, iRight];
19457         } else {
19458             this.padding = [iTop, iRight, iBot, iLeft];
19459         }
19460     },
19461
19462     /**
19463      * Stores the initial placement of the linked element.
19464      * @method setInitialPosition
19465      * @param {int} diffX   the X offset, default 0
19466      * @param {int} diffY   the Y offset, default 0
19467      */
19468     setInitPosition: function(diffX, diffY) {
19469         var el = this.getEl();
19470
19471         if (!this.DDM.verifyEl(el)) {
19472             return;
19473         }
19474
19475         var dx = diffX || 0;
19476         var dy = diffY || 0;
19477
19478         var p = Dom.getXY( el );
19479
19480         this.initPageX = p[0] - dx;
19481         this.initPageY = p[1] - dy;
19482
19483         this.lastPageX = p[0];
19484         this.lastPageY = p[1];
19485
19486
19487         this.setStartPosition(p);
19488     },
19489
19490     /**
19491      * Sets the start position of the element.  This is set when the obj
19492      * is initialized, the reset when a drag is started.
19493      * @method setStartPosition
19494      * @param pos current position (from previous lookup)
19495      * @private
19496      */
19497     setStartPosition: function(pos) {
19498         var p = pos || Dom.getXY( this.getEl() );
19499         this.deltaSetXY = null;
19500
19501         this.startPageX = p[0];
19502         this.startPageY = p[1];
19503     },
19504
19505     /**
19506      * Add this instance to a group of related drag/drop objects.  All
19507      * instances belong to at least one group, and can belong to as many
19508      * groups as needed.
19509      * @method addToGroup
19510      * @param sGroup {string} the name of the group
19511      */
19512     addToGroup: function(sGroup) {
19513         this.groups[sGroup] = true;
19514         this.DDM.regDragDrop(this, sGroup);
19515     },
19516
19517     /**
19518      * Remove's this instance from the supplied interaction group
19519      * @method removeFromGroup
19520      * @param {string}  sGroup  The group to drop
19521      */
19522     removeFromGroup: function(sGroup) {
19523         if (this.groups[sGroup]) {
19524             delete this.groups[sGroup];
19525         }
19526
19527         this.DDM.removeDDFromGroup(this, sGroup);
19528     },
19529
19530     /**
19531      * Allows you to specify that an element other than the linked element
19532      * will be moved with the cursor during a drag
19533      * @method setDragElId
19534      * @param id {string} the id of the element that will be used to initiate the drag
19535      */
19536     setDragElId: function(id) {
19537         this.dragElId = id;
19538     },
19539
19540     /**
19541      * Allows you to specify a child of the linked element that should be
19542      * used to initiate the drag operation.  An example of this would be if
19543      * you have a content div with text and links.  Clicking anywhere in the
19544      * content area would normally start the drag operation.  Use this method
19545      * to specify that an element inside of the content div is the element
19546      * that starts the drag operation.
19547      * @method setHandleElId
19548      * @param id {string} the id of the element that will be used to
19549      * initiate the drag.
19550      */
19551     setHandleElId: function(id) {
19552         if (typeof id !== "string") {
19553             id = Roo.id(id);
19554         }
19555         this.handleElId = id;
19556         this.DDM.regHandle(this.id, id);
19557     },
19558
19559     /**
19560      * Allows you to set an element outside of the linked element as a drag
19561      * handle
19562      * @method setOuterHandleElId
19563      * @param id the id of the element that will be used to initiate the drag
19564      */
19565     setOuterHandleElId: function(id) {
19566         if (typeof id !== "string") {
19567             id = Roo.id(id);
19568         }
19569         Event.on(id, "mousedown",
19570                 this.handleMouseDown, this);
19571         this.setHandleElId(id);
19572
19573         this.hasOuterHandles = true;
19574     },
19575
19576     /**
19577      * Remove all drag and drop hooks for this element
19578      * @method unreg
19579      */
19580     unreg: function() {
19581         Event.un(this.id, "mousedown",
19582                 this.handleMouseDown);
19583         Event.un(this.id, "touchstart",
19584                 this.handleMouseDown);
19585         this._domRef = null;
19586         this.DDM._remove(this);
19587     },
19588
19589     destroy : function(){
19590         this.unreg();
19591     },
19592
19593     /**
19594      * Returns true if this instance is locked, or the drag drop mgr is locked
19595      * (meaning that all drag/drop is disabled on the page.)
19596      * @method isLocked
19597      * @return {boolean} true if this obj or all drag/drop is locked, else
19598      * false
19599      */
19600     isLocked: function() {
19601         return (this.DDM.isLocked() || this.locked);
19602     },
19603
19604     /**
19605      * Fired when this object is clicked
19606      * @method handleMouseDown
19607      * @param {Event} e
19608      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19609      * @private
19610      */
19611     handleMouseDown: function(e, oDD){
19612      
19613         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19614             //Roo.log('not touch/ button !=0');
19615             return;
19616         }
19617         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19618             return; // double touch..
19619         }
19620         
19621
19622         if (this.isLocked()) {
19623             //Roo.log('locked');
19624             return;
19625         }
19626
19627         this.DDM.refreshCache(this.groups);
19628 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19629         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19630         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19631             //Roo.log('no outer handes or not over target');
19632                 // do nothing.
19633         } else {
19634 //            Roo.log('check validator');
19635             if (this.clickValidator(e)) {
19636 //                Roo.log('validate success');
19637                 // set the initial element position
19638                 this.setStartPosition();
19639
19640
19641                 this.b4MouseDown(e);
19642                 this.onMouseDown(e);
19643
19644                 this.DDM.handleMouseDown(e, this);
19645
19646                 this.DDM.stopEvent(e);
19647             } else {
19648
19649
19650             }
19651         }
19652     },
19653
19654     clickValidator: function(e) {
19655         var target = e.getTarget();
19656         return ( this.isValidHandleChild(target) &&
19657                     (this.id == this.handleElId ||
19658                         this.DDM.handleWasClicked(target, this.id)) );
19659     },
19660
19661     /**
19662      * Allows you to specify a tag name that should not start a drag operation
19663      * when clicked.  This is designed to facilitate embedding links within a
19664      * drag handle that do something other than start the drag.
19665      * @method addInvalidHandleType
19666      * @param {string} tagName the type of element to exclude
19667      */
19668     addInvalidHandleType: function(tagName) {
19669         var type = tagName.toUpperCase();
19670         this.invalidHandleTypes[type] = type;
19671     },
19672
19673     /**
19674      * Lets you to specify an element id for a child of a drag handle
19675      * that should not initiate a drag
19676      * @method addInvalidHandleId
19677      * @param {string} id the element id of the element you wish to ignore
19678      */
19679     addInvalidHandleId: function(id) {
19680         if (typeof id !== "string") {
19681             id = Roo.id(id);
19682         }
19683         this.invalidHandleIds[id] = id;
19684     },
19685
19686     /**
19687      * Lets you specify a css class of elements that will not initiate a drag
19688      * @method addInvalidHandleClass
19689      * @param {string} cssClass the class of the elements you wish to ignore
19690      */
19691     addInvalidHandleClass: function(cssClass) {
19692         this.invalidHandleClasses.push(cssClass);
19693     },
19694
19695     /**
19696      * Unsets an excluded tag name set by addInvalidHandleType
19697      * @method removeInvalidHandleType
19698      * @param {string} tagName the type of element to unexclude
19699      */
19700     removeInvalidHandleType: function(tagName) {
19701         var type = tagName.toUpperCase();
19702         // this.invalidHandleTypes[type] = null;
19703         delete this.invalidHandleTypes[type];
19704     },
19705
19706     /**
19707      * Unsets an invalid handle id
19708      * @method removeInvalidHandleId
19709      * @param {string} id the id of the element to re-enable
19710      */
19711     removeInvalidHandleId: function(id) {
19712         if (typeof id !== "string") {
19713             id = Roo.id(id);
19714         }
19715         delete this.invalidHandleIds[id];
19716     },
19717
19718     /**
19719      * Unsets an invalid css class
19720      * @method removeInvalidHandleClass
19721      * @param {string} cssClass the class of the element(s) you wish to
19722      * re-enable
19723      */
19724     removeInvalidHandleClass: function(cssClass) {
19725         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19726             if (this.invalidHandleClasses[i] == cssClass) {
19727                 delete this.invalidHandleClasses[i];
19728             }
19729         }
19730     },
19731
19732     /**
19733      * Checks the tag exclusion list to see if this click should be ignored
19734      * @method isValidHandleChild
19735      * @param {HTMLElement} node the HTMLElement to evaluate
19736      * @return {boolean} true if this is a valid tag type, false if not
19737      */
19738     isValidHandleChild: function(node) {
19739
19740         var valid = true;
19741         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19742         var nodeName;
19743         try {
19744             nodeName = node.nodeName.toUpperCase();
19745         } catch(e) {
19746             nodeName = node.nodeName;
19747         }
19748         valid = valid && !this.invalidHandleTypes[nodeName];
19749         valid = valid && !this.invalidHandleIds[node.id];
19750
19751         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19752             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19753         }
19754
19755
19756         return valid;
19757
19758     },
19759
19760     /**
19761      * Create the array of horizontal tick marks if an interval was specified
19762      * in setXConstraint().
19763      * @method setXTicks
19764      * @private
19765      */
19766     setXTicks: function(iStartX, iTickSize) {
19767         this.xTicks = [];
19768         this.xTickSize = iTickSize;
19769
19770         var tickMap = {};
19771
19772         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19773             if (!tickMap[i]) {
19774                 this.xTicks[this.xTicks.length] = i;
19775                 tickMap[i] = true;
19776             }
19777         }
19778
19779         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19780             if (!tickMap[i]) {
19781                 this.xTicks[this.xTicks.length] = i;
19782                 tickMap[i] = true;
19783             }
19784         }
19785
19786         this.xTicks.sort(this.DDM.numericSort) ;
19787     },
19788
19789     /**
19790      * Create the array of vertical tick marks if an interval was specified in
19791      * setYConstraint().
19792      * @method setYTicks
19793      * @private
19794      */
19795     setYTicks: function(iStartY, iTickSize) {
19796         this.yTicks = [];
19797         this.yTickSize = iTickSize;
19798
19799         var tickMap = {};
19800
19801         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19802             if (!tickMap[i]) {
19803                 this.yTicks[this.yTicks.length] = i;
19804                 tickMap[i] = true;
19805             }
19806         }
19807
19808         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19809             if (!tickMap[i]) {
19810                 this.yTicks[this.yTicks.length] = i;
19811                 tickMap[i] = true;
19812             }
19813         }
19814
19815         this.yTicks.sort(this.DDM.numericSort) ;
19816     },
19817
19818     /**
19819      * By default, the element can be dragged any place on the screen.  Use
19820      * this method to limit the horizontal travel of the element.  Pass in
19821      * 0,0 for the parameters if you want to lock the drag to the y axis.
19822      * @method setXConstraint
19823      * @param {int} iLeft the number of pixels the element can move to the left
19824      * @param {int} iRight the number of pixels the element can move to the
19825      * right
19826      * @param {int} iTickSize optional parameter for specifying that the
19827      * element
19828      * should move iTickSize pixels at a time.
19829      */
19830     setXConstraint: function(iLeft, iRight, iTickSize) {
19831         this.leftConstraint = iLeft;
19832         this.rightConstraint = iRight;
19833
19834         this.minX = this.initPageX - iLeft;
19835         this.maxX = this.initPageX + iRight;
19836         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19837
19838         this.constrainX = true;
19839     },
19840
19841     /**
19842      * Clears any constraints applied to this instance.  Also clears ticks
19843      * since they can't exist independent of a constraint at this time.
19844      * @method clearConstraints
19845      */
19846     clearConstraints: function() {
19847         this.constrainX = false;
19848         this.constrainY = false;
19849         this.clearTicks();
19850     },
19851
19852     /**
19853      * Clears any tick interval defined for this instance
19854      * @method clearTicks
19855      */
19856     clearTicks: function() {
19857         this.xTicks = null;
19858         this.yTicks = null;
19859         this.xTickSize = 0;
19860         this.yTickSize = 0;
19861     },
19862
19863     /**
19864      * By default, the element can be dragged any place on the screen.  Set
19865      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19866      * parameters if you want to lock the drag to the x axis.
19867      * @method setYConstraint
19868      * @param {int} iUp the number of pixels the element can move up
19869      * @param {int} iDown the number of pixels the element can move down
19870      * @param {int} iTickSize optional parameter for specifying that the
19871      * element should move iTickSize pixels at a time.
19872      */
19873     setYConstraint: function(iUp, iDown, iTickSize) {
19874         this.topConstraint = iUp;
19875         this.bottomConstraint = iDown;
19876
19877         this.minY = this.initPageY - iUp;
19878         this.maxY = this.initPageY + iDown;
19879         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19880
19881         this.constrainY = true;
19882
19883     },
19884
19885     /**
19886      * resetConstraints must be called if you manually reposition a dd element.
19887      * @method resetConstraints
19888      * @param {boolean} maintainOffset
19889      */
19890     resetConstraints: function() {
19891
19892
19893         // Maintain offsets if necessary
19894         if (this.initPageX || this.initPageX === 0) {
19895             // figure out how much this thing has moved
19896             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19897             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19898
19899             this.setInitPosition(dx, dy);
19900
19901         // This is the first time we have detected the element's position
19902         } else {
19903             this.setInitPosition();
19904         }
19905
19906         if (this.constrainX) {
19907             this.setXConstraint( this.leftConstraint,
19908                                  this.rightConstraint,
19909                                  this.xTickSize        );
19910         }
19911
19912         if (this.constrainY) {
19913             this.setYConstraint( this.topConstraint,
19914                                  this.bottomConstraint,
19915                                  this.yTickSize         );
19916         }
19917     },
19918
19919     /**
19920      * Normally the drag element is moved pixel by pixel, but we can specify
19921      * that it move a number of pixels at a time.  This method resolves the
19922      * location when we have it set up like this.
19923      * @method getTick
19924      * @param {int} val where we want to place the object
19925      * @param {int[]} tickArray sorted array of valid points
19926      * @return {int} the closest tick
19927      * @private
19928      */
19929     getTick: function(val, tickArray) {
19930
19931         if (!tickArray) {
19932             // If tick interval is not defined, it is effectively 1 pixel,
19933             // so we return the value passed to us.
19934             return val;
19935         } else if (tickArray[0] >= val) {
19936             // The value is lower than the first tick, so we return the first
19937             // tick.
19938             return tickArray[0];
19939         } else {
19940             for (var i=0, len=tickArray.length; i<len; ++i) {
19941                 var next = i + 1;
19942                 if (tickArray[next] && tickArray[next] >= val) {
19943                     var diff1 = val - tickArray[i];
19944                     var diff2 = tickArray[next] - val;
19945                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19946                 }
19947             }
19948
19949             // The value is larger than the last tick, so we return the last
19950             // tick.
19951             return tickArray[tickArray.length - 1];
19952         }
19953     },
19954
19955     /**
19956      * toString method
19957      * @method toString
19958      * @return {string} string representation of the dd obj
19959      */
19960     toString: function() {
19961         return ("DragDrop " + this.id);
19962     }
19963
19964 });
19965
19966 })();
19967 /*
19968  * Based on:
19969  * Ext JS Library 1.1.1
19970  * Copyright(c) 2006-2007, Ext JS, LLC.
19971  *
19972  * Originally Released Under LGPL - original licence link has changed is not relivant.
19973  *
19974  * Fork - LGPL
19975  * <script type="text/javascript">
19976  */
19977
19978
19979 /**
19980  * The drag and drop utility provides a framework for building drag and drop
19981  * applications.  In addition to enabling drag and drop for specific elements,
19982  * the drag and drop elements are tracked by the manager class, and the
19983  * interactions between the various elements are tracked during the drag and
19984  * the implementing code is notified about these important moments.
19985  */
19986
19987 // Only load the library once.  Rewriting the manager class would orphan
19988 // existing drag and drop instances.
19989 if (!Roo.dd.DragDropMgr) {
19990
19991 /**
19992  * @class Roo.dd.DragDropMgr
19993  * DragDropMgr is a singleton that tracks the element interaction for
19994  * all DragDrop items in the window.  Generally, you will not call
19995  * this class directly, but it does have helper methods that could
19996  * be useful in your DragDrop implementations.
19997  * @singleton
19998  */
19999 Roo.dd.DragDropMgr = function() {
20000
20001     var Event = Roo.EventManager;
20002
20003     return {
20004
20005         /**
20006          * Two dimensional Array of registered DragDrop objects.  The first
20007          * dimension is the DragDrop item group, the second the DragDrop
20008          * object.
20009          * @property ids
20010          * @type {string: string}
20011          * @private
20012          * @static
20013          */
20014         ids: {},
20015
20016         /**
20017          * Array of element ids defined as drag handles.  Used to determine
20018          * if the element that generated the mousedown event is actually the
20019          * handle and not the html element itself.
20020          * @property handleIds
20021          * @type {string: string}
20022          * @private
20023          * @static
20024          */
20025         handleIds: {},
20026
20027         /**
20028          * the DragDrop object that is currently being dragged
20029          * @property dragCurrent
20030          * @type DragDrop
20031          * @private
20032          * @static
20033          **/
20034         dragCurrent: null,
20035
20036         /**
20037          * the DragDrop object(s) that are being hovered over
20038          * @property dragOvers
20039          * @type Array
20040          * @private
20041          * @static
20042          */
20043         dragOvers: {},
20044
20045         /**
20046          * the X distance between the cursor and the object being dragged
20047          * @property deltaX
20048          * @type int
20049          * @private
20050          * @static
20051          */
20052         deltaX: 0,
20053
20054         /**
20055          * the Y distance between the cursor and the object being dragged
20056          * @property deltaY
20057          * @type int
20058          * @private
20059          * @static
20060          */
20061         deltaY: 0,
20062
20063         /**
20064          * Flag to determine if we should prevent the default behavior of the
20065          * events we define. By default this is true, but this can be set to
20066          * false if you need the default behavior (not recommended)
20067          * @property preventDefault
20068          * @type boolean
20069          * @static
20070          */
20071         preventDefault: true,
20072
20073         /**
20074          * Flag to determine if we should stop the propagation of the events
20075          * we generate. This is true by default but you may want to set it to
20076          * false if the html element contains other features that require the
20077          * mouse click.
20078          * @property stopPropagation
20079          * @type boolean
20080          * @static
20081          */
20082         stopPropagation: true,
20083
20084         /**
20085          * Internal flag that is set to true when drag and drop has been
20086          * intialized
20087          * @property initialized
20088          * @private
20089          * @static
20090          */
20091         initalized: false,
20092
20093         /**
20094          * All drag and drop can be disabled.
20095          * @property locked
20096          * @private
20097          * @static
20098          */
20099         locked: false,
20100
20101         /**
20102          * Called the first time an element is registered.
20103          * @method init
20104          * @private
20105          * @static
20106          */
20107         init: function() {
20108             this.initialized = true;
20109         },
20110
20111         /**
20112          * In point mode, drag and drop interaction is defined by the
20113          * location of the cursor during the drag/drop
20114          * @property POINT
20115          * @type int
20116          * @static
20117          */
20118         POINT: 0,
20119
20120         /**
20121          * In intersect mode, drag and drop interactio nis defined by the
20122          * overlap of two or more drag and drop objects.
20123          * @property INTERSECT
20124          * @type int
20125          * @static
20126          */
20127         INTERSECT: 1,
20128
20129         /**
20130          * The current drag and drop mode.  Default: POINT
20131          * @property mode
20132          * @type int
20133          * @static
20134          */
20135         mode: 0,
20136
20137         /**
20138          * Runs method on all drag and drop objects
20139          * @method _execOnAll
20140          * @private
20141          * @static
20142          */
20143         _execOnAll: function(sMethod, args) {
20144             for (var i in this.ids) {
20145                 for (var j in this.ids[i]) {
20146                     var oDD = this.ids[i][j];
20147                     if (! this.isTypeOfDD(oDD)) {
20148                         continue;
20149                     }
20150                     oDD[sMethod].apply(oDD, args);
20151                 }
20152             }
20153         },
20154
20155         /**
20156          * Drag and drop initialization.  Sets up the global event handlers
20157          * @method _onLoad
20158          * @private
20159          * @static
20160          */
20161         _onLoad: function() {
20162
20163             this.init();
20164
20165             if (!Roo.isTouch) {
20166                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20167                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20168             }
20169             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20170             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20171             
20172             Event.on(window,   "unload",    this._onUnload, this, true);
20173             Event.on(window,   "resize",    this._onResize, this, true);
20174             // Event.on(window,   "mouseout",    this._test);
20175
20176         },
20177
20178         /**
20179          * Reset constraints on all drag and drop objs
20180          * @method _onResize
20181          * @private
20182          * @static
20183          */
20184         _onResize: function(e) {
20185             this._execOnAll("resetConstraints", []);
20186         },
20187
20188         /**
20189          * Lock all drag and drop functionality
20190          * @method lock
20191          * @static
20192          */
20193         lock: function() { this.locked = true; },
20194
20195         /**
20196          * Unlock all drag and drop functionality
20197          * @method unlock
20198          * @static
20199          */
20200         unlock: function() { this.locked = false; },
20201
20202         /**
20203          * Is drag and drop locked?
20204          * @method isLocked
20205          * @return {boolean} True if drag and drop is locked, false otherwise.
20206          * @static
20207          */
20208         isLocked: function() { return this.locked; },
20209
20210         /**
20211          * Location cache that is set for all drag drop objects when a drag is
20212          * initiated, cleared when the drag is finished.
20213          * @property locationCache
20214          * @private
20215          * @static
20216          */
20217         locationCache: {},
20218
20219         /**
20220          * Set useCache to false if you want to force object the lookup of each
20221          * drag and drop linked element constantly during a drag.
20222          * @property useCache
20223          * @type boolean
20224          * @static
20225          */
20226         useCache: true,
20227
20228         /**
20229          * The number of pixels that the mouse needs to move after the
20230          * mousedown before the drag is initiated.  Default=3;
20231          * @property clickPixelThresh
20232          * @type int
20233          * @static
20234          */
20235         clickPixelThresh: 3,
20236
20237         /**
20238          * The number of milliseconds after the mousedown event to initiate the
20239          * drag if we don't get a mouseup event. Default=1000
20240          * @property clickTimeThresh
20241          * @type int
20242          * @static
20243          */
20244         clickTimeThresh: 350,
20245
20246         /**
20247          * Flag that indicates that either the drag pixel threshold or the
20248          * mousdown time threshold has been met
20249          * @property dragThreshMet
20250          * @type boolean
20251          * @private
20252          * @static
20253          */
20254         dragThreshMet: false,
20255
20256         /**
20257          * Timeout used for the click time threshold
20258          * @property clickTimeout
20259          * @type Object
20260          * @private
20261          * @static
20262          */
20263         clickTimeout: null,
20264
20265         /**
20266          * The X position of the mousedown event stored for later use when a
20267          * drag threshold is met.
20268          * @property startX
20269          * @type int
20270          * @private
20271          * @static
20272          */
20273         startX: 0,
20274
20275         /**
20276          * The Y position of the mousedown event stored for later use when a
20277          * drag threshold is met.
20278          * @property startY
20279          * @type int
20280          * @private
20281          * @static
20282          */
20283         startY: 0,
20284
20285         /**
20286          * Each DragDrop instance must be registered with the DragDropMgr.
20287          * This is executed in DragDrop.init()
20288          * @method regDragDrop
20289          * @param {DragDrop} oDD the DragDrop object to register
20290          * @param {String} sGroup the name of the group this element belongs to
20291          * @static
20292          */
20293         regDragDrop: function(oDD, sGroup) {
20294             if (!this.initialized) { this.init(); }
20295
20296             if (!this.ids[sGroup]) {
20297                 this.ids[sGroup] = {};
20298             }
20299             this.ids[sGroup][oDD.id] = oDD;
20300         },
20301
20302         /**
20303          * Removes the supplied dd instance from the supplied group. Executed
20304          * by DragDrop.removeFromGroup, so don't call this function directly.
20305          * @method removeDDFromGroup
20306          * @private
20307          * @static
20308          */
20309         removeDDFromGroup: function(oDD, sGroup) {
20310             if (!this.ids[sGroup]) {
20311                 this.ids[sGroup] = {};
20312             }
20313
20314             var obj = this.ids[sGroup];
20315             if (obj && obj[oDD.id]) {
20316                 delete obj[oDD.id];
20317             }
20318         },
20319
20320         /**
20321          * Unregisters a drag and drop item.  This is executed in
20322          * DragDrop.unreg, use that method instead of calling this directly.
20323          * @method _remove
20324          * @private
20325          * @static
20326          */
20327         _remove: function(oDD) {
20328             for (var g in oDD.groups) {
20329                 if (g && this.ids[g][oDD.id]) {
20330                     delete this.ids[g][oDD.id];
20331                 }
20332             }
20333             delete this.handleIds[oDD.id];
20334         },
20335
20336         /**
20337          * Each DragDrop handle element must be registered.  This is done
20338          * automatically when executing DragDrop.setHandleElId()
20339          * @method regHandle
20340          * @param {String} sDDId the DragDrop id this element is a handle for
20341          * @param {String} sHandleId the id of the element that is the drag
20342          * handle
20343          * @static
20344          */
20345         regHandle: function(sDDId, sHandleId) {
20346             if (!this.handleIds[sDDId]) {
20347                 this.handleIds[sDDId] = {};
20348             }
20349             this.handleIds[sDDId][sHandleId] = sHandleId;
20350         },
20351
20352         /**
20353          * Utility function to determine if a given element has been
20354          * registered as a drag drop item.
20355          * @method isDragDrop
20356          * @param {String} id the element id to check
20357          * @return {boolean} true if this element is a DragDrop item,
20358          * false otherwise
20359          * @static
20360          */
20361         isDragDrop: function(id) {
20362             return ( this.getDDById(id) ) ? true : false;
20363         },
20364
20365         /**
20366          * Returns the drag and drop instances that are in all groups the
20367          * passed in instance belongs to.
20368          * @method getRelated
20369          * @param {DragDrop} p_oDD the obj to get related data for
20370          * @param {boolean} bTargetsOnly if true, only return targetable objs
20371          * @return {DragDrop[]} the related instances
20372          * @static
20373          */
20374         getRelated: function(p_oDD, bTargetsOnly) {
20375             var oDDs = [];
20376             for (var i in p_oDD.groups) {
20377                 for (j in this.ids[i]) {
20378                     var dd = this.ids[i][j];
20379                     if (! this.isTypeOfDD(dd)) {
20380                         continue;
20381                     }
20382                     if (!bTargetsOnly || dd.isTarget) {
20383                         oDDs[oDDs.length] = dd;
20384                     }
20385                 }
20386             }
20387
20388             return oDDs;
20389         },
20390
20391         /**
20392          * Returns true if the specified dd target is a legal target for
20393          * the specifice drag obj
20394          * @method isLegalTarget
20395          * @param {DragDrop} the drag obj
20396          * @param {DragDrop} the target
20397          * @return {boolean} true if the target is a legal target for the
20398          * dd obj
20399          * @static
20400          */
20401         isLegalTarget: function (oDD, oTargetDD) {
20402             var targets = this.getRelated(oDD, true);
20403             for (var i=0, len=targets.length;i<len;++i) {
20404                 if (targets[i].id == oTargetDD.id) {
20405                     return true;
20406                 }
20407             }
20408
20409             return false;
20410         },
20411
20412         /**
20413          * My goal is to be able to transparently determine if an object is
20414          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20415          * returns "object", oDD.constructor.toString() always returns
20416          * "DragDrop" and not the name of the subclass.  So for now it just
20417          * evaluates a well-known variable in DragDrop.
20418          * @method isTypeOfDD
20419          * @param {Object} the object to evaluate
20420          * @return {boolean} true if typeof oDD = DragDrop
20421          * @static
20422          */
20423         isTypeOfDD: function (oDD) {
20424             return (oDD && oDD.__ygDragDrop);
20425         },
20426
20427         /**
20428          * Utility function to determine if a given element has been
20429          * registered as a drag drop handle for the given Drag Drop object.
20430          * @method isHandle
20431          * @param {String} id the element id to check
20432          * @return {boolean} true if this element is a DragDrop handle, false
20433          * otherwise
20434          * @static
20435          */
20436         isHandle: function(sDDId, sHandleId) {
20437             return ( this.handleIds[sDDId] &&
20438                             this.handleIds[sDDId][sHandleId] );
20439         },
20440
20441         /**
20442          * Returns the DragDrop instance for a given id
20443          * @method getDDById
20444          * @param {String} id the id of the DragDrop object
20445          * @return {DragDrop} the drag drop object, null if it is not found
20446          * @static
20447          */
20448         getDDById: function(id) {
20449             for (var i in this.ids) {
20450                 if (this.ids[i][id]) {
20451                     return this.ids[i][id];
20452                 }
20453             }
20454             return null;
20455         },
20456
20457         /**
20458          * Fired after a registered DragDrop object gets the mousedown event.
20459          * Sets up the events required to track the object being dragged
20460          * @method handleMouseDown
20461          * @param {Event} e the event
20462          * @param oDD the DragDrop object being dragged
20463          * @private
20464          * @static
20465          */
20466         handleMouseDown: function(e, oDD) {
20467             if(Roo.QuickTips){
20468                 Roo.QuickTips.disable();
20469             }
20470             this.currentTarget = e.getTarget();
20471
20472             this.dragCurrent = oDD;
20473
20474             var el = oDD.getEl();
20475
20476             // track start position
20477             this.startX = e.getPageX();
20478             this.startY = e.getPageY();
20479
20480             this.deltaX = this.startX - el.offsetLeft;
20481             this.deltaY = this.startY - el.offsetTop;
20482
20483             this.dragThreshMet = false;
20484
20485             this.clickTimeout = setTimeout(
20486                     function() {
20487                         var DDM = Roo.dd.DDM;
20488                         DDM.startDrag(DDM.startX, DDM.startY);
20489                     },
20490                     this.clickTimeThresh );
20491         },
20492
20493         /**
20494          * Fired when either the drag pixel threshol or the mousedown hold
20495          * time threshold has been met.
20496          * @method startDrag
20497          * @param x {int} the X position of the original mousedown
20498          * @param y {int} the Y position of the original mousedown
20499          * @static
20500          */
20501         startDrag: function(x, y) {
20502             clearTimeout(this.clickTimeout);
20503             if (this.dragCurrent) {
20504                 this.dragCurrent.b4StartDrag(x, y);
20505                 this.dragCurrent.startDrag(x, y);
20506             }
20507             this.dragThreshMet = true;
20508         },
20509
20510         /**
20511          * Internal function to handle the mouseup event.  Will be invoked
20512          * from the context of the document.
20513          * @method handleMouseUp
20514          * @param {Event} e the event
20515          * @private
20516          * @static
20517          */
20518         handleMouseUp: function(e) {
20519
20520             if(Roo.QuickTips){
20521                 Roo.QuickTips.enable();
20522             }
20523             if (! this.dragCurrent) {
20524                 return;
20525             }
20526
20527             clearTimeout(this.clickTimeout);
20528
20529             if (this.dragThreshMet) {
20530                 this.fireEvents(e, true);
20531             } else {
20532             }
20533
20534             this.stopDrag(e);
20535
20536             this.stopEvent(e);
20537         },
20538
20539         /**
20540          * Utility to stop event propagation and event default, if these
20541          * features are turned on.
20542          * @method stopEvent
20543          * @param {Event} e the event as returned by this.getEvent()
20544          * @static
20545          */
20546         stopEvent: function(e){
20547             if(this.stopPropagation) {
20548                 e.stopPropagation();
20549             }
20550
20551             if (this.preventDefault) {
20552                 e.preventDefault();
20553             }
20554         },
20555
20556         /**
20557          * Internal function to clean up event handlers after the drag
20558          * operation is complete
20559          * @method stopDrag
20560          * @param {Event} e the event
20561          * @private
20562          * @static
20563          */
20564         stopDrag: function(e) {
20565             // Fire the drag end event for the item that was dragged
20566             if (this.dragCurrent) {
20567                 if (this.dragThreshMet) {
20568                     this.dragCurrent.b4EndDrag(e);
20569                     this.dragCurrent.endDrag(e);
20570                 }
20571
20572                 this.dragCurrent.onMouseUp(e);
20573             }
20574
20575             this.dragCurrent = null;
20576             this.dragOvers = {};
20577         },
20578
20579         /**
20580          * Internal function to handle the mousemove event.  Will be invoked
20581          * from the context of the html element.
20582          *
20583          * @TODO figure out what we can do about mouse events lost when the
20584          * user drags objects beyond the window boundary.  Currently we can
20585          * detect this in internet explorer by verifying that the mouse is
20586          * down during the mousemove event.  Firefox doesn't give us the
20587          * button state on the mousemove event.
20588          * @method handleMouseMove
20589          * @param {Event} e the event
20590          * @private
20591          * @static
20592          */
20593         handleMouseMove: function(e) {
20594             if (! this.dragCurrent) {
20595                 return true;
20596             }
20597
20598             // var button = e.which || e.button;
20599
20600             // check for IE mouseup outside of page boundary
20601             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20602                 this.stopEvent(e);
20603                 return this.handleMouseUp(e);
20604             }
20605
20606             if (!this.dragThreshMet) {
20607                 var diffX = Math.abs(this.startX - e.getPageX());
20608                 var diffY = Math.abs(this.startY - e.getPageY());
20609                 if (diffX > this.clickPixelThresh ||
20610                             diffY > this.clickPixelThresh) {
20611                     this.startDrag(this.startX, this.startY);
20612                 }
20613             }
20614
20615             if (this.dragThreshMet) {
20616                 this.dragCurrent.b4Drag(e);
20617                 this.dragCurrent.onDrag(e);
20618                 if(!this.dragCurrent.moveOnly){
20619                     this.fireEvents(e, false);
20620                 }
20621             }
20622
20623             this.stopEvent(e);
20624
20625             return true;
20626         },
20627
20628         /**
20629          * Iterates over all of the DragDrop elements to find ones we are
20630          * hovering over or dropping on
20631          * @method fireEvents
20632          * @param {Event} e the event
20633          * @param {boolean} isDrop is this a drop op or a mouseover op?
20634          * @private
20635          * @static
20636          */
20637         fireEvents: function(e, isDrop) {
20638             var dc = this.dragCurrent;
20639
20640             // If the user did the mouse up outside of the window, we could
20641             // get here even though we have ended the drag.
20642             if (!dc || dc.isLocked()) {
20643                 return;
20644             }
20645
20646             var pt = e.getPoint();
20647
20648             // cache the previous dragOver array
20649             var oldOvers = [];
20650
20651             var outEvts   = [];
20652             var overEvts  = [];
20653             var dropEvts  = [];
20654             var enterEvts = [];
20655
20656             // Check to see if the object(s) we were hovering over is no longer
20657             // being hovered over so we can fire the onDragOut event
20658             for (var i in this.dragOvers) {
20659
20660                 var ddo = this.dragOvers[i];
20661
20662                 if (! this.isTypeOfDD(ddo)) {
20663                     continue;
20664                 }
20665
20666                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20667                     outEvts.push( ddo );
20668                 }
20669
20670                 oldOvers[i] = true;
20671                 delete this.dragOvers[i];
20672             }
20673
20674             for (var sGroup in dc.groups) {
20675
20676                 if ("string" != typeof sGroup) {
20677                     continue;
20678                 }
20679
20680                 for (i in this.ids[sGroup]) {
20681                     var oDD = this.ids[sGroup][i];
20682                     if (! this.isTypeOfDD(oDD)) {
20683                         continue;
20684                     }
20685
20686                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20687                         if (this.isOverTarget(pt, oDD, this.mode)) {
20688                             // look for drop interactions
20689                             if (isDrop) {
20690                                 dropEvts.push( oDD );
20691                             // look for drag enter and drag over interactions
20692                             } else {
20693
20694                                 // initial drag over: dragEnter fires
20695                                 if (!oldOvers[oDD.id]) {
20696                                     enterEvts.push( oDD );
20697                                 // subsequent drag overs: dragOver fires
20698                                 } else {
20699                                     overEvts.push( oDD );
20700                                 }
20701
20702                                 this.dragOvers[oDD.id] = oDD;
20703                             }
20704                         }
20705                     }
20706                 }
20707             }
20708
20709             if (this.mode) {
20710                 if (outEvts.length) {
20711                     dc.b4DragOut(e, outEvts);
20712                     dc.onDragOut(e, outEvts);
20713                 }
20714
20715                 if (enterEvts.length) {
20716                     dc.onDragEnter(e, enterEvts);
20717                 }
20718
20719                 if (overEvts.length) {
20720                     dc.b4DragOver(e, overEvts);
20721                     dc.onDragOver(e, overEvts);
20722                 }
20723
20724                 if (dropEvts.length) {
20725                     dc.b4DragDrop(e, dropEvts);
20726                     dc.onDragDrop(e, dropEvts);
20727                 }
20728
20729             } else {
20730                 // fire dragout events
20731                 var len = 0;
20732                 for (i=0, len=outEvts.length; i<len; ++i) {
20733                     dc.b4DragOut(e, outEvts[i].id);
20734                     dc.onDragOut(e, outEvts[i].id);
20735                 }
20736
20737                 // fire enter events
20738                 for (i=0,len=enterEvts.length; i<len; ++i) {
20739                     // dc.b4DragEnter(e, oDD.id);
20740                     dc.onDragEnter(e, enterEvts[i].id);
20741                 }
20742
20743                 // fire over events
20744                 for (i=0,len=overEvts.length; i<len; ++i) {
20745                     dc.b4DragOver(e, overEvts[i].id);
20746                     dc.onDragOver(e, overEvts[i].id);
20747                 }
20748
20749                 // fire drop events
20750                 for (i=0, len=dropEvts.length; i<len; ++i) {
20751                     dc.b4DragDrop(e, dropEvts[i].id);
20752                     dc.onDragDrop(e, dropEvts[i].id);
20753                 }
20754
20755             }
20756
20757             // notify about a drop that did not find a target
20758             if (isDrop && !dropEvts.length) {
20759                 dc.onInvalidDrop(e);
20760             }
20761
20762         },
20763
20764         /**
20765          * Helper function for getting the best match from the list of drag
20766          * and drop objects returned by the drag and drop events when we are
20767          * in INTERSECT mode.  It returns either the first object that the
20768          * cursor is over, or the object that has the greatest overlap with
20769          * the dragged element.
20770          * @method getBestMatch
20771          * @param  {DragDrop[]} dds The array of drag and drop objects
20772          * targeted
20773          * @return {DragDrop}       The best single match
20774          * @static
20775          */
20776         getBestMatch: function(dds) {
20777             var winner = null;
20778             // Return null if the input is not what we expect
20779             //if (!dds || !dds.length || dds.length == 0) {
20780                // winner = null;
20781             // If there is only one item, it wins
20782             //} else if (dds.length == 1) {
20783
20784             var len = dds.length;
20785
20786             if (len == 1) {
20787                 winner = dds[0];
20788             } else {
20789                 // Loop through the targeted items
20790                 for (var i=0; i<len; ++i) {
20791                     var dd = dds[i];
20792                     // If the cursor is over the object, it wins.  If the
20793                     // cursor is over multiple matches, the first one we come
20794                     // to wins.
20795                     if (dd.cursorIsOver) {
20796                         winner = dd;
20797                         break;
20798                     // Otherwise the object with the most overlap wins
20799                     } else {
20800                         if (!winner ||
20801                             winner.overlap.getArea() < dd.overlap.getArea()) {
20802                             winner = dd;
20803                         }
20804                     }
20805                 }
20806             }
20807
20808             return winner;
20809         },
20810
20811         /**
20812          * Refreshes the cache of the top-left and bottom-right points of the
20813          * drag and drop objects in the specified group(s).  This is in the
20814          * format that is stored in the drag and drop instance, so typical
20815          * usage is:
20816          * <code>
20817          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20818          * </code>
20819          * Alternatively:
20820          * <code>
20821          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20822          * </code>
20823          * @TODO this really should be an indexed array.  Alternatively this
20824          * method could accept both.
20825          * @method refreshCache
20826          * @param {Object} groups an associative array of groups to refresh
20827          * @static
20828          */
20829         refreshCache: function(groups) {
20830             for (var sGroup in groups) {
20831                 if ("string" != typeof sGroup) {
20832                     continue;
20833                 }
20834                 for (var i in this.ids[sGroup]) {
20835                     var oDD = this.ids[sGroup][i];
20836
20837                     if (this.isTypeOfDD(oDD)) {
20838                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20839                         var loc = this.getLocation(oDD);
20840                         if (loc) {
20841                             this.locationCache[oDD.id] = loc;
20842                         } else {
20843                             delete this.locationCache[oDD.id];
20844                             // this will unregister the drag and drop object if
20845                             // the element is not in a usable state
20846                             // oDD.unreg();
20847                         }
20848                     }
20849                 }
20850             }
20851         },
20852
20853         /**
20854          * This checks to make sure an element exists and is in the DOM.  The
20855          * main purpose is to handle cases where innerHTML is used to remove
20856          * drag and drop objects from the DOM.  IE provides an 'unspecified
20857          * error' when trying to access the offsetParent of such an element
20858          * @method verifyEl
20859          * @param {HTMLElement} el the element to check
20860          * @return {boolean} true if the element looks usable
20861          * @static
20862          */
20863         verifyEl: function(el) {
20864             if (el) {
20865                 var parent;
20866                 if(Roo.isIE){
20867                     try{
20868                         parent = el.offsetParent;
20869                     }catch(e){}
20870                 }else{
20871                     parent = el.offsetParent;
20872                 }
20873                 if (parent) {
20874                     return true;
20875                 }
20876             }
20877
20878             return false;
20879         },
20880
20881         /**
20882          * Returns a Region object containing the drag and drop element's position
20883          * and size, including the padding configured for it
20884          * @method getLocation
20885          * @param {DragDrop} oDD the drag and drop object to get the
20886          *                       location for
20887          * @return {Roo.lib.Region} a Region object representing the total area
20888          *                             the element occupies, including any padding
20889          *                             the instance is configured for.
20890          * @static
20891          */
20892         getLocation: function(oDD) {
20893             if (! this.isTypeOfDD(oDD)) {
20894                 return null;
20895             }
20896
20897             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20898
20899             try {
20900                 pos= Roo.lib.Dom.getXY(el);
20901             } catch (e) { }
20902
20903             if (!pos) {
20904                 return null;
20905             }
20906
20907             x1 = pos[0];
20908             x2 = x1 + el.offsetWidth;
20909             y1 = pos[1];
20910             y2 = y1 + el.offsetHeight;
20911
20912             t = y1 - oDD.padding[0];
20913             r = x2 + oDD.padding[1];
20914             b = y2 + oDD.padding[2];
20915             l = x1 - oDD.padding[3];
20916
20917             return new Roo.lib.Region( t, r, b, l );
20918         },
20919
20920         /**
20921          * Checks the cursor location to see if it over the target
20922          * @method isOverTarget
20923          * @param {Roo.lib.Point} pt The point to evaluate
20924          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20925          * @return {boolean} true if the mouse is over the target
20926          * @private
20927          * @static
20928          */
20929         isOverTarget: function(pt, oTarget, intersect) {
20930             // use cache if available
20931             var loc = this.locationCache[oTarget.id];
20932             if (!loc || !this.useCache) {
20933                 loc = this.getLocation(oTarget);
20934                 this.locationCache[oTarget.id] = loc;
20935
20936             }
20937
20938             if (!loc) {
20939                 return false;
20940             }
20941
20942             oTarget.cursorIsOver = loc.contains( pt );
20943
20944             // DragDrop is using this as a sanity check for the initial mousedown
20945             // in this case we are done.  In POINT mode, if the drag obj has no
20946             // contraints, we are also done. Otherwise we need to evaluate the
20947             // location of the target as related to the actual location of the
20948             // dragged element.
20949             var dc = this.dragCurrent;
20950             if (!dc || !dc.getTargetCoord ||
20951                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20952                 return oTarget.cursorIsOver;
20953             }
20954
20955             oTarget.overlap = null;
20956
20957             // Get the current location of the drag element, this is the
20958             // location of the mouse event less the delta that represents
20959             // where the original mousedown happened on the element.  We
20960             // need to consider constraints and ticks as well.
20961             var pos = dc.getTargetCoord(pt.x, pt.y);
20962
20963             var el = dc.getDragEl();
20964             var curRegion = new Roo.lib.Region( pos.y,
20965                                                    pos.x + el.offsetWidth,
20966                                                    pos.y + el.offsetHeight,
20967                                                    pos.x );
20968
20969             var overlap = curRegion.intersect(loc);
20970
20971             if (overlap) {
20972                 oTarget.overlap = overlap;
20973                 return (intersect) ? true : oTarget.cursorIsOver;
20974             } else {
20975                 return false;
20976             }
20977         },
20978
20979         /**
20980          * unload event handler
20981          * @method _onUnload
20982          * @private
20983          * @static
20984          */
20985         _onUnload: function(e, me) {
20986             Roo.dd.DragDropMgr.unregAll();
20987         },
20988
20989         /**
20990          * Cleans up the drag and drop events and objects.
20991          * @method unregAll
20992          * @private
20993          * @static
20994          */
20995         unregAll: function() {
20996
20997             if (this.dragCurrent) {
20998                 this.stopDrag();
20999                 this.dragCurrent = null;
21000             }
21001
21002             this._execOnAll("unreg", []);
21003
21004             for (i in this.elementCache) {
21005                 delete this.elementCache[i];
21006             }
21007
21008             this.elementCache = {};
21009             this.ids = {};
21010         },
21011
21012         /**
21013          * A cache of DOM elements
21014          * @property elementCache
21015          * @private
21016          * @static
21017          */
21018         elementCache: {},
21019
21020         /**
21021          * Get the wrapper for the DOM element specified
21022          * @method getElWrapper
21023          * @param {String} id the id of the element to get
21024          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21025          * @private
21026          * @deprecated This wrapper isn't that useful
21027          * @static
21028          */
21029         getElWrapper: function(id) {
21030             var oWrapper = this.elementCache[id];
21031             if (!oWrapper || !oWrapper.el) {
21032                 oWrapper = this.elementCache[id] =
21033                     new this.ElementWrapper(Roo.getDom(id));
21034             }
21035             return oWrapper;
21036         },
21037
21038         /**
21039          * Returns the actual DOM element
21040          * @method getElement
21041          * @param {String} id the id of the elment to get
21042          * @return {Object} The element
21043          * @deprecated use Roo.getDom instead
21044          * @static
21045          */
21046         getElement: function(id) {
21047             return Roo.getDom(id);
21048         },
21049
21050         /**
21051          * Returns the style property for the DOM element (i.e.,
21052          * document.getElById(id).style)
21053          * @method getCss
21054          * @param {String} id the id of the elment to get
21055          * @return {Object} The style property of the element
21056          * @deprecated use Roo.getDom instead
21057          * @static
21058          */
21059         getCss: function(id) {
21060             var el = Roo.getDom(id);
21061             return (el) ? el.style : null;
21062         },
21063
21064         /**
21065          * Inner class for cached elements
21066          * @class DragDropMgr.ElementWrapper
21067          * @for DragDropMgr
21068          * @private
21069          * @deprecated
21070          */
21071         ElementWrapper: function(el) {
21072                 /**
21073                  * The element
21074                  * @property el
21075                  */
21076                 this.el = el || null;
21077                 /**
21078                  * The element id
21079                  * @property id
21080                  */
21081                 this.id = this.el && el.id;
21082                 /**
21083                  * A reference to the style property
21084                  * @property css
21085                  */
21086                 this.css = this.el && el.style;
21087             },
21088
21089         /**
21090          * Returns the X position of an html element
21091          * @method getPosX
21092          * @param el the element for which to get the position
21093          * @return {int} the X coordinate
21094          * @for DragDropMgr
21095          * @deprecated use Roo.lib.Dom.getX instead
21096          * @static
21097          */
21098         getPosX: function(el) {
21099             return Roo.lib.Dom.getX(el);
21100         },
21101
21102         /**
21103          * Returns the Y position of an html element
21104          * @method getPosY
21105          * @param el the element for which to get the position
21106          * @return {int} the Y coordinate
21107          * @deprecated use Roo.lib.Dom.getY instead
21108          * @static
21109          */
21110         getPosY: function(el) {
21111             return Roo.lib.Dom.getY(el);
21112         },
21113
21114         /**
21115          * Swap two nodes.  In IE, we use the native method, for others we
21116          * emulate the IE behavior
21117          * @method swapNode
21118          * @param n1 the first node to swap
21119          * @param n2 the other node to swap
21120          * @static
21121          */
21122         swapNode: function(n1, n2) {
21123             if (n1.swapNode) {
21124                 n1.swapNode(n2);
21125             } else {
21126                 var p = n2.parentNode;
21127                 var s = n2.nextSibling;
21128
21129                 if (s == n1) {
21130                     p.insertBefore(n1, n2);
21131                 } else if (n2 == n1.nextSibling) {
21132                     p.insertBefore(n2, n1);
21133                 } else {
21134                     n1.parentNode.replaceChild(n2, n1);
21135                     p.insertBefore(n1, s);
21136                 }
21137             }
21138         },
21139
21140         /**
21141          * Returns the current scroll position
21142          * @method getScroll
21143          * @private
21144          * @static
21145          */
21146         getScroll: function () {
21147             var t, l, dde=document.documentElement, db=document.body;
21148             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21149                 t = dde.scrollTop;
21150                 l = dde.scrollLeft;
21151             } else if (db) {
21152                 t = db.scrollTop;
21153                 l = db.scrollLeft;
21154             } else {
21155
21156             }
21157             return { top: t, left: l };
21158         },
21159
21160         /**
21161          * Returns the specified element style property
21162          * @method getStyle
21163          * @param {HTMLElement} el          the element
21164          * @param {string}      styleProp   the style property
21165          * @return {string} The value of the style property
21166          * @deprecated use Roo.lib.Dom.getStyle
21167          * @static
21168          */
21169         getStyle: function(el, styleProp) {
21170             return Roo.fly(el).getStyle(styleProp);
21171         },
21172
21173         /**
21174          * Gets the scrollTop
21175          * @method getScrollTop
21176          * @return {int} the document's scrollTop
21177          * @static
21178          */
21179         getScrollTop: function () { return this.getScroll().top; },
21180
21181         /**
21182          * Gets the scrollLeft
21183          * @method getScrollLeft
21184          * @return {int} the document's scrollTop
21185          * @static
21186          */
21187         getScrollLeft: function () { return this.getScroll().left; },
21188
21189         /**
21190          * Sets the x/y position of an element to the location of the
21191          * target element.
21192          * @method moveToEl
21193          * @param {HTMLElement} moveEl      The element to move
21194          * @param {HTMLElement} targetEl    The position reference element
21195          * @static
21196          */
21197         moveToEl: function (moveEl, targetEl) {
21198             var aCoord = Roo.lib.Dom.getXY(targetEl);
21199             Roo.lib.Dom.setXY(moveEl, aCoord);
21200         },
21201
21202         /**
21203          * Numeric array sort function
21204          * @method numericSort
21205          * @static
21206          */
21207         numericSort: function(a, b) { return (a - b); },
21208
21209         /**
21210          * Internal counter
21211          * @property _timeoutCount
21212          * @private
21213          * @static
21214          */
21215         _timeoutCount: 0,
21216
21217         /**
21218          * Trying to make the load order less important.  Without this we get
21219          * an error if this file is loaded before the Event Utility.
21220          * @method _addListeners
21221          * @private
21222          * @static
21223          */
21224         _addListeners: function() {
21225             var DDM = Roo.dd.DDM;
21226             if ( Roo.lib.Event && document ) {
21227                 DDM._onLoad();
21228             } else {
21229                 if (DDM._timeoutCount > 2000) {
21230                 } else {
21231                     setTimeout(DDM._addListeners, 10);
21232                     if (document && document.body) {
21233                         DDM._timeoutCount += 1;
21234                     }
21235                 }
21236             }
21237         },
21238
21239         /**
21240          * Recursively searches the immediate parent and all child nodes for
21241          * the handle element in order to determine wheter or not it was
21242          * clicked.
21243          * @method handleWasClicked
21244          * @param node the html element to inspect
21245          * @static
21246          */
21247         handleWasClicked: function(node, id) {
21248             if (this.isHandle(id, node.id)) {
21249                 return true;
21250             } else {
21251                 // check to see if this is a text node child of the one we want
21252                 var p = node.parentNode;
21253
21254                 while (p) {
21255                     if (this.isHandle(id, p.id)) {
21256                         return true;
21257                     } else {
21258                         p = p.parentNode;
21259                     }
21260                 }
21261             }
21262
21263             return false;
21264         }
21265
21266     };
21267
21268 }();
21269
21270 // shorter alias, save a few bytes
21271 Roo.dd.DDM = Roo.dd.DragDropMgr;
21272 Roo.dd.DDM._addListeners();
21273
21274 }/*
21275  * Based on:
21276  * Ext JS Library 1.1.1
21277  * Copyright(c) 2006-2007, Ext JS, LLC.
21278  *
21279  * Originally Released Under LGPL - original licence link has changed is not relivant.
21280  *
21281  * Fork - LGPL
21282  * <script type="text/javascript">
21283  */
21284
21285 /**
21286  * @class Roo.dd.DD
21287  * A DragDrop implementation where the linked element follows the
21288  * mouse cursor during a drag.
21289  * @extends Roo.dd.DragDrop
21290  * @constructor
21291  * @param {String} id the id of the linked element
21292  * @param {String} sGroup the group of related DragDrop items
21293  * @param {object} config an object containing configurable attributes
21294  *                Valid properties for DD:
21295  *                    scroll
21296  */
21297 Roo.dd.DD = function(id, sGroup, config) {
21298     if (id) {
21299         this.init(id, sGroup, config);
21300     }
21301 };
21302
21303 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21304
21305     /**
21306      * When set to true, the utility automatically tries to scroll the browser
21307      * window wehn a drag and drop element is dragged near the viewport boundary.
21308      * Defaults to true.
21309      * @property scroll
21310      * @type boolean
21311      */
21312     scroll: true,
21313
21314     /**
21315      * Sets the pointer offset to the distance between the linked element's top
21316      * left corner and the location the element was clicked
21317      * @method autoOffset
21318      * @param {int} iPageX the X coordinate of the click
21319      * @param {int} iPageY the Y coordinate of the click
21320      */
21321     autoOffset: function(iPageX, iPageY) {
21322         var x = iPageX - this.startPageX;
21323         var y = iPageY - this.startPageY;
21324         this.setDelta(x, y);
21325     },
21326
21327     /**
21328      * Sets the pointer offset.  You can call this directly to force the
21329      * offset to be in a particular location (e.g., pass in 0,0 to set it
21330      * to the center of the object)
21331      * @method setDelta
21332      * @param {int} iDeltaX the distance from the left
21333      * @param {int} iDeltaY the distance from the top
21334      */
21335     setDelta: function(iDeltaX, iDeltaY) {
21336         this.deltaX = iDeltaX;
21337         this.deltaY = iDeltaY;
21338     },
21339
21340     /**
21341      * Sets the drag element to the location of the mousedown or click event,
21342      * maintaining the cursor location relative to the location on the element
21343      * that was clicked.  Override this if you want to place the element in a
21344      * location other than where the cursor is.
21345      * @method setDragElPos
21346      * @param {int} iPageX the X coordinate of the mousedown or drag event
21347      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21348      */
21349     setDragElPos: function(iPageX, iPageY) {
21350         // the first time we do this, we are going to check to make sure
21351         // the element has css positioning
21352
21353         var el = this.getDragEl();
21354         this.alignElWithMouse(el, iPageX, iPageY);
21355     },
21356
21357     /**
21358      * Sets the element to the location of the mousedown or click event,
21359      * maintaining the cursor location relative to the location on the element
21360      * that was clicked.  Override this if you want to place the element in a
21361      * location other than where the cursor is.
21362      * @method alignElWithMouse
21363      * @param {HTMLElement} el the element to move
21364      * @param {int} iPageX the X coordinate of the mousedown or drag event
21365      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21366      */
21367     alignElWithMouse: function(el, iPageX, iPageY) {
21368         var oCoord = this.getTargetCoord(iPageX, iPageY);
21369         var fly = el.dom ? el : Roo.fly(el);
21370         if (!this.deltaSetXY) {
21371             var aCoord = [oCoord.x, oCoord.y];
21372             fly.setXY(aCoord);
21373             var newLeft = fly.getLeft(true);
21374             var newTop  = fly.getTop(true);
21375             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21376         } else {
21377             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21378         }
21379
21380         this.cachePosition(oCoord.x, oCoord.y);
21381         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21382         return oCoord;
21383     },
21384
21385     /**
21386      * Saves the most recent position so that we can reset the constraints and
21387      * tick marks on-demand.  We need to know this so that we can calculate the
21388      * number of pixels the element is offset from its original position.
21389      * @method cachePosition
21390      * @param iPageX the current x position (optional, this just makes it so we
21391      * don't have to look it up again)
21392      * @param iPageY the current y position (optional, this just makes it so we
21393      * don't have to look it up again)
21394      */
21395     cachePosition: function(iPageX, iPageY) {
21396         if (iPageX) {
21397             this.lastPageX = iPageX;
21398             this.lastPageY = iPageY;
21399         } else {
21400             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21401             this.lastPageX = aCoord[0];
21402             this.lastPageY = aCoord[1];
21403         }
21404     },
21405
21406     /**
21407      * Auto-scroll the window if the dragged object has been moved beyond the
21408      * visible window boundary.
21409      * @method autoScroll
21410      * @param {int} x the drag element's x position
21411      * @param {int} y the drag element's y position
21412      * @param {int} h the height of the drag element
21413      * @param {int} w the width of the drag element
21414      * @private
21415      */
21416     autoScroll: function(x, y, h, w) {
21417
21418         if (this.scroll) {
21419             // The client height
21420             var clientH = Roo.lib.Dom.getViewWidth();
21421
21422             // The client width
21423             var clientW = Roo.lib.Dom.getViewHeight();
21424
21425             // The amt scrolled down
21426             var st = this.DDM.getScrollTop();
21427
21428             // The amt scrolled right
21429             var sl = this.DDM.getScrollLeft();
21430
21431             // Location of the bottom of the element
21432             var bot = h + y;
21433
21434             // Location of the right of the element
21435             var right = w + x;
21436
21437             // The distance from the cursor to the bottom of the visible area,
21438             // adjusted so that we don't scroll if the cursor is beyond the
21439             // element drag constraints
21440             var toBot = (clientH + st - y - this.deltaY);
21441
21442             // The distance from the cursor to the right of the visible area
21443             var toRight = (clientW + sl - x - this.deltaX);
21444
21445
21446             // How close to the edge the cursor must be before we scroll
21447             // var thresh = (document.all) ? 100 : 40;
21448             var thresh = 40;
21449
21450             // How many pixels to scroll per autoscroll op.  This helps to reduce
21451             // clunky scrolling. IE is more sensitive about this ... it needs this
21452             // value to be higher.
21453             var scrAmt = (document.all) ? 80 : 30;
21454
21455             // Scroll down if we are near the bottom of the visible page and the
21456             // obj extends below the crease
21457             if ( bot > clientH && toBot < thresh ) {
21458                 window.scrollTo(sl, st + scrAmt);
21459             }
21460
21461             // Scroll up if the window is scrolled down and the top of the object
21462             // goes above the top border
21463             if ( y < st && st > 0 && y - st < thresh ) {
21464                 window.scrollTo(sl, st - scrAmt);
21465             }
21466
21467             // Scroll right if the obj is beyond the right border and the cursor is
21468             // near the border.
21469             if ( right > clientW && toRight < thresh ) {
21470                 window.scrollTo(sl + scrAmt, st);
21471             }
21472
21473             // Scroll left if the window has been scrolled to the right and the obj
21474             // extends past the left border
21475             if ( x < sl && sl > 0 && x - sl < thresh ) {
21476                 window.scrollTo(sl - scrAmt, st);
21477             }
21478         }
21479     },
21480
21481     /**
21482      * Finds the location the element should be placed if we want to move
21483      * it to where the mouse location less the click offset would place us.
21484      * @method getTargetCoord
21485      * @param {int} iPageX the X coordinate of the click
21486      * @param {int} iPageY the Y coordinate of the click
21487      * @return an object that contains the coordinates (Object.x and Object.y)
21488      * @private
21489      */
21490     getTargetCoord: function(iPageX, iPageY) {
21491
21492
21493         var x = iPageX - this.deltaX;
21494         var y = iPageY - this.deltaY;
21495
21496         if (this.constrainX) {
21497             if (x < this.minX) { x = this.minX; }
21498             if (x > this.maxX) { x = this.maxX; }
21499         }
21500
21501         if (this.constrainY) {
21502             if (y < this.minY) { y = this.minY; }
21503             if (y > this.maxY) { y = this.maxY; }
21504         }
21505
21506         x = this.getTick(x, this.xTicks);
21507         y = this.getTick(y, this.yTicks);
21508
21509
21510         return {x:x, y:y};
21511     },
21512
21513     /*
21514      * Sets up config options specific to this class. Overrides
21515      * Roo.dd.DragDrop, but all versions of this method through the
21516      * inheritance chain are called
21517      */
21518     applyConfig: function() {
21519         Roo.dd.DD.superclass.applyConfig.call(this);
21520         this.scroll = (this.config.scroll !== false);
21521     },
21522
21523     /*
21524      * Event that fires prior to the onMouseDown event.  Overrides
21525      * Roo.dd.DragDrop.
21526      */
21527     b4MouseDown: function(e) {
21528         // this.resetConstraints();
21529         this.autoOffset(e.getPageX(),
21530                             e.getPageY());
21531     },
21532
21533     /*
21534      * Event that fires prior to the onDrag event.  Overrides
21535      * Roo.dd.DragDrop.
21536      */
21537     b4Drag: function(e) {
21538         this.setDragElPos(e.getPageX(),
21539                             e.getPageY());
21540     },
21541
21542     toString: function() {
21543         return ("DD " + this.id);
21544     }
21545
21546     //////////////////////////////////////////////////////////////////////////
21547     // Debugging ygDragDrop events that can be overridden
21548     //////////////////////////////////////////////////////////////////////////
21549     /*
21550     startDrag: function(x, y) {
21551     },
21552
21553     onDrag: function(e) {
21554     },
21555
21556     onDragEnter: function(e, id) {
21557     },
21558
21559     onDragOver: function(e, id) {
21560     },
21561
21562     onDragOut: function(e, id) {
21563     },
21564
21565     onDragDrop: function(e, id) {
21566     },
21567
21568     endDrag: function(e) {
21569     }
21570
21571     */
21572
21573 });/*
21574  * Based on:
21575  * Ext JS Library 1.1.1
21576  * Copyright(c) 2006-2007, Ext JS, LLC.
21577  *
21578  * Originally Released Under LGPL - original licence link has changed is not relivant.
21579  *
21580  * Fork - LGPL
21581  * <script type="text/javascript">
21582  */
21583
21584 /**
21585  * @class Roo.dd.DDProxy
21586  * A DragDrop implementation that inserts an empty, bordered div into
21587  * the document that follows the cursor during drag operations.  At the time of
21588  * the click, the frame div is resized to the dimensions of the linked html
21589  * element, and moved to the exact location of the linked element.
21590  *
21591  * References to the "frame" element refer to the single proxy element that
21592  * was created to be dragged in place of all DDProxy elements on the
21593  * page.
21594  *
21595  * @extends Roo.dd.DD
21596  * @constructor
21597  * @param {String} id the id of the linked html element
21598  * @param {String} sGroup the group of related DragDrop objects
21599  * @param {object} config an object containing configurable attributes
21600  *                Valid properties for DDProxy in addition to those in DragDrop:
21601  *                   resizeFrame, centerFrame, dragElId
21602  */
21603 Roo.dd.DDProxy = function(id, sGroup, config) {
21604     if (id) {
21605         this.init(id, sGroup, config);
21606         this.initFrame();
21607     }
21608 };
21609
21610 /**
21611  * The default drag frame div id
21612  * @property Roo.dd.DDProxy.dragElId
21613  * @type String
21614  * @static
21615  */
21616 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21617
21618 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21619
21620     /**
21621      * By default we resize the drag frame to be the same size as the element
21622      * we want to drag (this is to get the frame effect).  We can turn it off
21623      * if we want a different behavior.
21624      * @property resizeFrame
21625      * @type boolean
21626      */
21627     resizeFrame: true,
21628
21629     /**
21630      * By default the frame is positioned exactly where the drag element is, so
21631      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21632      * you do not have constraints on the obj is to have the drag frame centered
21633      * around the cursor.  Set centerFrame to true for this effect.
21634      * @property centerFrame
21635      * @type boolean
21636      */
21637     centerFrame: false,
21638
21639     /**
21640      * Creates the proxy element if it does not yet exist
21641      * @method createFrame
21642      */
21643     createFrame: function() {
21644         var self = this;
21645         var body = document.body;
21646
21647         if (!body || !body.firstChild) {
21648             setTimeout( function() { self.createFrame(); }, 50 );
21649             return;
21650         }
21651
21652         var div = this.getDragEl();
21653
21654         if (!div) {
21655             div    = document.createElement("div");
21656             div.id = this.dragElId;
21657             var s  = div.style;
21658
21659             s.position   = "absolute";
21660             s.visibility = "hidden";
21661             s.cursor     = "move";
21662             s.border     = "2px solid #aaa";
21663             s.zIndex     = 999;
21664
21665             // appendChild can blow up IE if invoked prior to the window load event
21666             // while rendering a table.  It is possible there are other scenarios
21667             // that would cause this to happen as well.
21668             body.insertBefore(div, body.firstChild);
21669         }
21670     },
21671
21672     /**
21673      * Initialization for the drag frame element.  Must be called in the
21674      * constructor of all subclasses
21675      * @method initFrame
21676      */
21677     initFrame: function() {
21678         this.createFrame();
21679     },
21680
21681     applyConfig: function() {
21682         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21683
21684         this.resizeFrame = (this.config.resizeFrame !== false);
21685         this.centerFrame = (this.config.centerFrame);
21686         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21687     },
21688
21689     /**
21690      * Resizes the drag frame to the dimensions of the clicked object, positions
21691      * it over the object, and finally displays it
21692      * @method showFrame
21693      * @param {int} iPageX X click position
21694      * @param {int} iPageY Y click position
21695      * @private
21696      */
21697     showFrame: function(iPageX, iPageY) {
21698         var el = this.getEl();
21699         var dragEl = this.getDragEl();
21700         var s = dragEl.style;
21701
21702         this._resizeProxy();
21703
21704         if (this.centerFrame) {
21705             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21706                            Math.round(parseInt(s.height, 10)/2) );
21707         }
21708
21709         this.setDragElPos(iPageX, iPageY);
21710
21711         Roo.fly(dragEl).show();
21712     },
21713
21714     /**
21715      * The proxy is automatically resized to the dimensions of the linked
21716      * element when a drag is initiated, unless resizeFrame is set to false
21717      * @method _resizeProxy
21718      * @private
21719      */
21720     _resizeProxy: function() {
21721         if (this.resizeFrame) {
21722             var el = this.getEl();
21723             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21724         }
21725     },
21726
21727     // overrides Roo.dd.DragDrop
21728     b4MouseDown: function(e) {
21729         var x = e.getPageX();
21730         var y = e.getPageY();
21731         this.autoOffset(x, y);
21732         this.setDragElPos(x, y);
21733     },
21734
21735     // overrides Roo.dd.DragDrop
21736     b4StartDrag: function(x, y) {
21737         // show the drag frame
21738         this.showFrame(x, y);
21739     },
21740
21741     // overrides Roo.dd.DragDrop
21742     b4EndDrag: function(e) {
21743         Roo.fly(this.getDragEl()).hide();
21744     },
21745
21746     // overrides Roo.dd.DragDrop
21747     // By default we try to move the element to the last location of the frame.
21748     // This is so that the default behavior mirrors that of Roo.dd.DD.
21749     endDrag: function(e) {
21750
21751         var lel = this.getEl();
21752         var del = this.getDragEl();
21753
21754         // Show the drag frame briefly so we can get its position
21755         del.style.visibility = "";
21756
21757         this.beforeMove();
21758         // Hide the linked element before the move to get around a Safari
21759         // rendering bug.
21760         lel.style.visibility = "hidden";
21761         Roo.dd.DDM.moveToEl(lel, del);
21762         del.style.visibility = "hidden";
21763         lel.style.visibility = "";
21764
21765         this.afterDrag();
21766     },
21767
21768     beforeMove : function(){
21769
21770     },
21771
21772     afterDrag : function(){
21773
21774     },
21775
21776     toString: function() {
21777         return ("DDProxy " + this.id);
21778     }
21779
21780 });
21781 /*
21782  * Based on:
21783  * Ext JS Library 1.1.1
21784  * Copyright(c) 2006-2007, Ext JS, LLC.
21785  *
21786  * Originally Released Under LGPL - original licence link has changed is not relivant.
21787  *
21788  * Fork - LGPL
21789  * <script type="text/javascript">
21790  */
21791
21792  /**
21793  * @class Roo.dd.DDTarget
21794  * A DragDrop implementation that does not move, but can be a drop
21795  * target.  You would get the same result by simply omitting implementation
21796  * for the event callbacks, but this way we reduce the processing cost of the
21797  * event listener and the callbacks.
21798  * @extends Roo.dd.DragDrop
21799  * @constructor
21800  * @param {String} id the id of the element that is a drop target
21801  * @param {String} sGroup the group of related DragDrop objects
21802  * @param {object} config an object containing configurable attributes
21803  *                 Valid properties for DDTarget in addition to those in
21804  *                 DragDrop:
21805  *                    none
21806  */
21807 Roo.dd.DDTarget = function(id, sGroup, config) {
21808     if (id) {
21809         this.initTarget(id, sGroup, config);
21810     }
21811     if (config && (config.listeners || config.events)) { 
21812         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21813             listeners : config.listeners || {}, 
21814             events : config.events || {} 
21815         });    
21816     }
21817 };
21818
21819 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21820 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21821     toString: function() {
21822         return ("DDTarget " + this.id);
21823     }
21824 });
21825 /*
21826  * Based on:
21827  * Ext JS Library 1.1.1
21828  * Copyright(c) 2006-2007, Ext JS, LLC.
21829  *
21830  * Originally Released Under LGPL - original licence link has changed is not relivant.
21831  *
21832  * Fork - LGPL
21833  * <script type="text/javascript">
21834  */
21835  
21836
21837 /**
21838  * @class Roo.dd.ScrollManager
21839  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21840  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21841  * @singleton
21842  */
21843 Roo.dd.ScrollManager = function(){
21844     var ddm = Roo.dd.DragDropMgr;
21845     var els = {};
21846     var dragEl = null;
21847     var proc = {};
21848     
21849     
21850     
21851     var onStop = function(e){
21852         dragEl = null;
21853         clearProc();
21854     };
21855     
21856     var triggerRefresh = function(){
21857         if(ddm.dragCurrent){
21858              ddm.refreshCache(ddm.dragCurrent.groups);
21859         }
21860     };
21861     
21862     var doScroll = function(){
21863         if(ddm.dragCurrent){
21864             var dds = Roo.dd.ScrollManager;
21865             if(!dds.animate){
21866                 if(proc.el.scroll(proc.dir, dds.increment)){
21867                     triggerRefresh();
21868                 }
21869             }else{
21870                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21871             }
21872         }
21873     };
21874     
21875     var clearProc = function(){
21876         if(proc.id){
21877             clearInterval(proc.id);
21878         }
21879         proc.id = 0;
21880         proc.el = null;
21881         proc.dir = "";
21882     };
21883     
21884     var startProc = function(el, dir){
21885          Roo.log('scroll startproc');
21886         clearProc();
21887         proc.el = el;
21888         proc.dir = dir;
21889         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21890     };
21891     
21892     var onFire = function(e, isDrop){
21893        
21894         if(isDrop || !ddm.dragCurrent){ return; }
21895         var dds = Roo.dd.ScrollManager;
21896         if(!dragEl || dragEl != ddm.dragCurrent){
21897             dragEl = ddm.dragCurrent;
21898             // refresh regions on drag start
21899             dds.refreshCache();
21900         }
21901         
21902         var xy = Roo.lib.Event.getXY(e);
21903         var pt = new Roo.lib.Point(xy[0], xy[1]);
21904         for(var id in els){
21905             var el = els[id], r = el._region;
21906             if(r && r.contains(pt) && el.isScrollable()){
21907                 if(r.bottom - pt.y <= dds.thresh){
21908                     if(proc.el != el){
21909                         startProc(el, "down");
21910                     }
21911                     return;
21912                 }else if(r.right - pt.x <= dds.thresh){
21913                     if(proc.el != el){
21914                         startProc(el, "left");
21915                     }
21916                     return;
21917                 }else if(pt.y - r.top <= dds.thresh){
21918                     if(proc.el != el){
21919                         startProc(el, "up");
21920                     }
21921                     return;
21922                 }else if(pt.x - r.left <= dds.thresh){
21923                     if(proc.el != el){
21924                         startProc(el, "right");
21925                     }
21926                     return;
21927                 }
21928             }
21929         }
21930         clearProc();
21931     };
21932     
21933     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21934     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21935     
21936     return {
21937         /**
21938          * Registers new overflow element(s) to auto scroll
21939          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21940          */
21941         register : function(el){
21942             if(el instanceof Array){
21943                 for(var i = 0, len = el.length; i < len; i++) {
21944                         this.register(el[i]);
21945                 }
21946             }else{
21947                 el = Roo.get(el);
21948                 els[el.id] = el;
21949             }
21950             Roo.dd.ScrollManager.els = els;
21951         },
21952         
21953         /**
21954          * Unregisters overflow element(s) so they are no longer scrolled
21955          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21956          */
21957         unregister : function(el){
21958             if(el instanceof Array){
21959                 for(var i = 0, len = el.length; i < len; i++) {
21960                         this.unregister(el[i]);
21961                 }
21962             }else{
21963                 el = Roo.get(el);
21964                 delete els[el.id];
21965             }
21966         },
21967         
21968         /**
21969          * The number of pixels from the edge of a container the pointer needs to be to 
21970          * trigger scrolling (defaults to 25)
21971          * @type Number
21972          */
21973         thresh : 25,
21974         
21975         /**
21976          * The number of pixels to scroll in each scroll increment (defaults to 50)
21977          * @type Number
21978          */
21979         increment : 100,
21980         
21981         /**
21982          * The frequency of scrolls in milliseconds (defaults to 500)
21983          * @type Number
21984          */
21985         frequency : 500,
21986         
21987         /**
21988          * True to animate the scroll (defaults to true)
21989          * @type Boolean
21990          */
21991         animate: true,
21992         
21993         /**
21994          * The animation duration in seconds - 
21995          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21996          * @type Number
21997          */
21998         animDuration: .4,
21999         
22000         /**
22001          * Manually trigger a cache refresh.
22002          */
22003         refreshCache : function(){
22004             for(var id in els){
22005                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22006                     els[id]._region = els[id].getRegion();
22007                 }
22008             }
22009         }
22010     };
22011 }();/*
22012  * Based on:
22013  * Ext JS Library 1.1.1
22014  * Copyright(c) 2006-2007, Ext JS, LLC.
22015  *
22016  * Originally Released Under LGPL - original licence link has changed is not relivant.
22017  *
22018  * Fork - LGPL
22019  * <script type="text/javascript">
22020  */
22021  
22022
22023 /**
22024  * @class Roo.dd.Registry
22025  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22026  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22027  * @singleton
22028  */
22029 Roo.dd.Registry = function(){
22030     var elements = {}; 
22031     var handles = {}; 
22032     var autoIdSeed = 0;
22033
22034     var getId = function(el, autogen){
22035         if(typeof el == "string"){
22036             return el;
22037         }
22038         var id = el.id;
22039         if(!id && autogen !== false){
22040             id = "roodd-" + (++autoIdSeed);
22041             el.id = id;
22042         }
22043         return id;
22044     };
22045     
22046     return {
22047     /**
22048      * Register a drag drop element
22049      * @param {String|HTMLElement} element The id or DOM node to register
22050      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22051      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22052      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22053      * populated in the data object (if applicable):
22054      * <pre>
22055 Value      Description<br />
22056 ---------  ------------------------------------------<br />
22057 handles    Array of DOM nodes that trigger dragging<br />
22058            for the element being registered<br />
22059 isHandle   True if the element passed in triggers<br />
22060            dragging itself, else false
22061 </pre>
22062      */
22063         register : function(el, data){
22064             data = data || {};
22065             if(typeof el == "string"){
22066                 el = document.getElementById(el);
22067             }
22068             data.ddel = el;
22069             elements[getId(el)] = data;
22070             if(data.isHandle !== false){
22071                 handles[data.ddel.id] = data;
22072             }
22073             if(data.handles){
22074                 var hs = data.handles;
22075                 for(var i = 0, len = hs.length; i < len; i++){
22076                         handles[getId(hs[i])] = data;
22077                 }
22078             }
22079         },
22080
22081     /**
22082      * Unregister a drag drop element
22083      * @param {String|HTMLElement}  element The id or DOM node to unregister
22084      */
22085         unregister : function(el){
22086             var id = getId(el, false);
22087             var data = elements[id];
22088             if(data){
22089                 delete elements[id];
22090                 if(data.handles){
22091                     var hs = data.handles;
22092                     for(var i = 0, len = hs.length; i < len; i++){
22093                         delete handles[getId(hs[i], false)];
22094                     }
22095                 }
22096             }
22097         },
22098
22099     /**
22100      * Returns the handle registered for a DOM Node by id
22101      * @param {String|HTMLElement} id The DOM node or id to look up
22102      * @return {Object} handle The custom handle data
22103      */
22104         getHandle : function(id){
22105             if(typeof id != "string"){ // must be element?
22106                 id = id.id;
22107             }
22108             return handles[id];
22109         },
22110
22111     /**
22112      * Returns the handle that is registered for the DOM node that is the target of the event
22113      * @param {Event} e The event
22114      * @return {Object} handle The custom handle data
22115      */
22116         getHandleFromEvent : function(e){
22117             var t = Roo.lib.Event.getTarget(e);
22118             return t ? handles[t.id] : null;
22119         },
22120
22121     /**
22122      * Returns a custom data object that is registered for a DOM node by id
22123      * @param {String|HTMLElement} id The DOM node or id to look up
22124      * @return {Object} data The custom data
22125      */
22126         getTarget : function(id){
22127             if(typeof id != "string"){ // must be element?
22128                 id = id.id;
22129             }
22130             return elements[id];
22131         },
22132
22133     /**
22134      * Returns a custom data object that is registered for the DOM node that is the target of the event
22135      * @param {Event} e The event
22136      * @return {Object} data The custom data
22137      */
22138         getTargetFromEvent : function(e){
22139             var t = Roo.lib.Event.getTarget(e);
22140             return t ? elements[t.id] || handles[t.id] : null;
22141         }
22142     };
22143 }();/*
22144  * Based on:
22145  * Ext JS Library 1.1.1
22146  * Copyright(c) 2006-2007, Ext JS, LLC.
22147  *
22148  * Originally Released Under LGPL - original licence link has changed is not relivant.
22149  *
22150  * Fork - LGPL
22151  * <script type="text/javascript">
22152  */
22153  
22154
22155 /**
22156  * @class Roo.dd.StatusProxy
22157  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22158  * default drag proxy used by all Roo.dd components.
22159  * @constructor
22160  * @param {Object} config
22161  */
22162 Roo.dd.StatusProxy = function(config){
22163     Roo.apply(this, config);
22164     this.id = this.id || Roo.id();
22165     this.el = new Roo.Layer({
22166         dh: {
22167             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22168                 {tag: "div", cls: "x-dd-drop-icon"},
22169                 {tag: "div", cls: "x-dd-drag-ghost"}
22170             ]
22171         }, 
22172         shadow: !config || config.shadow !== false
22173     });
22174     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22175     this.dropStatus = this.dropNotAllowed;
22176 };
22177
22178 Roo.dd.StatusProxy.prototype = {
22179     /**
22180      * @cfg {String} dropAllowed
22181      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22182      */
22183     dropAllowed : "x-dd-drop-ok",
22184     /**
22185      * @cfg {String} dropNotAllowed
22186      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22187      */
22188     dropNotAllowed : "x-dd-drop-nodrop",
22189
22190     /**
22191      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22192      * over the current target element.
22193      * @param {String} cssClass The css class for the new drop status indicator image
22194      */
22195     setStatus : function(cssClass){
22196         cssClass = cssClass || this.dropNotAllowed;
22197         if(this.dropStatus != cssClass){
22198             this.el.replaceClass(this.dropStatus, cssClass);
22199             this.dropStatus = cssClass;
22200         }
22201     },
22202
22203     /**
22204      * Resets the status indicator to the default dropNotAllowed value
22205      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22206      */
22207     reset : function(clearGhost){
22208         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22209         this.dropStatus = this.dropNotAllowed;
22210         if(clearGhost){
22211             this.ghost.update("");
22212         }
22213     },
22214
22215     /**
22216      * Updates the contents of the ghost element
22217      * @param {String} html The html that will replace the current innerHTML of the ghost element
22218      */
22219     update : function(html){
22220         if(typeof html == "string"){
22221             this.ghost.update(html);
22222         }else{
22223             this.ghost.update("");
22224             html.style.margin = "0";
22225             this.ghost.dom.appendChild(html);
22226         }
22227         // ensure float = none set?? cant remember why though.
22228         var el = this.ghost.dom.firstChild;
22229                 if(el){
22230                         Roo.fly(el).setStyle('float', 'none');
22231                 }
22232     },
22233     
22234     /**
22235      * Returns the underlying proxy {@link Roo.Layer}
22236      * @return {Roo.Layer} el
22237     */
22238     getEl : function(){
22239         return this.el;
22240     },
22241
22242     /**
22243      * Returns the ghost element
22244      * @return {Roo.Element} el
22245      */
22246     getGhost : function(){
22247         return this.ghost;
22248     },
22249
22250     /**
22251      * Hides the proxy
22252      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22253      */
22254     hide : function(clear){
22255         this.el.hide();
22256         if(clear){
22257             this.reset(true);
22258         }
22259     },
22260
22261     /**
22262      * Stops the repair animation if it's currently running
22263      */
22264     stop : function(){
22265         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22266             this.anim.stop();
22267         }
22268     },
22269
22270     /**
22271      * Displays this proxy
22272      */
22273     show : function(){
22274         this.el.show();
22275     },
22276
22277     /**
22278      * Force the Layer to sync its shadow and shim positions to the element
22279      */
22280     sync : function(){
22281         this.el.sync();
22282     },
22283
22284     /**
22285      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22286      * invalid drop operation by the item being dragged.
22287      * @param {Array} xy The XY position of the element ([x, y])
22288      * @param {Function} callback The function to call after the repair is complete
22289      * @param {Object} scope The scope in which to execute the callback
22290      */
22291     repair : function(xy, callback, scope){
22292         this.callback = callback;
22293         this.scope = scope;
22294         if(xy && this.animRepair !== false){
22295             this.el.addClass("x-dd-drag-repair");
22296             this.el.hideUnders(true);
22297             this.anim = this.el.shift({
22298                 duration: this.repairDuration || .5,
22299                 easing: 'easeOut',
22300                 xy: xy,
22301                 stopFx: true,
22302                 callback: this.afterRepair,
22303                 scope: this
22304             });
22305         }else{
22306             this.afterRepair();
22307         }
22308     },
22309
22310     // private
22311     afterRepair : function(){
22312         this.hide(true);
22313         if(typeof this.callback == "function"){
22314             this.callback.call(this.scope || this);
22315         }
22316         this.callback = null;
22317         this.scope = null;
22318     }
22319 };/*
22320  * Based on:
22321  * Ext JS Library 1.1.1
22322  * Copyright(c) 2006-2007, Ext JS, LLC.
22323  *
22324  * Originally Released Under LGPL - original licence link has changed is not relivant.
22325  *
22326  * Fork - LGPL
22327  * <script type="text/javascript">
22328  */
22329
22330 /**
22331  * @class Roo.dd.DragSource
22332  * @extends Roo.dd.DDProxy
22333  * A simple class that provides the basic implementation needed to make any element draggable.
22334  * @constructor
22335  * @param {String/HTMLElement/Element} el The container element
22336  * @param {Object} config
22337  */
22338 Roo.dd.DragSource = function(el, config){
22339     this.el = Roo.get(el);
22340     this.dragData = {};
22341     
22342     Roo.apply(this, config);
22343     
22344     if(!this.proxy){
22345         this.proxy = new Roo.dd.StatusProxy();
22346     }
22347
22348     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22349           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22350     
22351     this.dragging = false;
22352 };
22353
22354 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22355     /**
22356      * @cfg {String} dropAllowed
22357      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22358      */
22359     dropAllowed : "x-dd-drop-ok",
22360     /**
22361      * @cfg {String} dropNotAllowed
22362      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22363      */
22364     dropNotAllowed : "x-dd-drop-nodrop",
22365
22366     /**
22367      * Returns the data object associated with this drag source
22368      * @return {Object} data An object containing arbitrary data
22369      */
22370     getDragData : function(e){
22371         return this.dragData;
22372     },
22373
22374     // private
22375     onDragEnter : function(e, id){
22376         var target = Roo.dd.DragDropMgr.getDDById(id);
22377         this.cachedTarget = target;
22378         if(this.beforeDragEnter(target, e, id) !== false){
22379             if(target.isNotifyTarget){
22380                 var status = target.notifyEnter(this, e, this.dragData);
22381                 this.proxy.setStatus(status);
22382             }else{
22383                 this.proxy.setStatus(this.dropAllowed);
22384             }
22385             
22386             if(this.afterDragEnter){
22387                 /**
22388                  * An empty function by default, but provided so that you can perform a custom action
22389                  * when the dragged item enters the drop target by providing an implementation.
22390                  * @param {Roo.dd.DragDrop} target The drop target
22391                  * @param {Event} e The event object
22392                  * @param {String} id The id of the dragged element
22393                  * @method afterDragEnter
22394                  */
22395                 this.afterDragEnter(target, e, id);
22396             }
22397         }
22398     },
22399
22400     /**
22401      * An empty function by default, but provided so that you can perform a custom action
22402      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22403      * @param {Roo.dd.DragDrop} target The drop target
22404      * @param {Event} e The event object
22405      * @param {String} id The id of the dragged element
22406      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22407      */
22408     beforeDragEnter : function(target, e, id){
22409         return true;
22410     },
22411
22412     // private
22413     alignElWithMouse: function() {
22414         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22415         this.proxy.sync();
22416     },
22417
22418     // private
22419     onDragOver : function(e, id){
22420         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22421         if(this.beforeDragOver(target, e, id) !== false){
22422             if(target.isNotifyTarget){
22423                 var status = target.notifyOver(this, e, this.dragData);
22424                 this.proxy.setStatus(status);
22425             }
22426
22427             if(this.afterDragOver){
22428                 /**
22429                  * An empty function by default, but provided so that you can perform a custom action
22430                  * while the dragged item is over the drop target by providing an implementation.
22431                  * @param {Roo.dd.DragDrop} target The drop target
22432                  * @param {Event} e The event object
22433                  * @param {String} id The id of the dragged element
22434                  * @method afterDragOver
22435                  */
22436                 this.afterDragOver(target, e, id);
22437             }
22438         }
22439     },
22440
22441     /**
22442      * An empty function by default, but provided so that you can perform a custom action
22443      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22444      * @param {Roo.dd.DragDrop} target The drop target
22445      * @param {Event} e The event object
22446      * @param {String} id The id of the dragged element
22447      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22448      */
22449     beforeDragOver : function(target, e, id){
22450         return true;
22451     },
22452
22453     // private
22454     onDragOut : function(e, id){
22455         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22456         if(this.beforeDragOut(target, e, id) !== false){
22457             if(target.isNotifyTarget){
22458                 target.notifyOut(this, e, this.dragData);
22459             }
22460             this.proxy.reset();
22461             if(this.afterDragOut){
22462                 /**
22463                  * An empty function by default, but provided so that you can perform a custom action
22464                  * after the dragged item is dragged out of the target without dropping.
22465                  * @param {Roo.dd.DragDrop} target The drop target
22466                  * @param {Event} e The event object
22467                  * @param {String} id The id of the dragged element
22468                  * @method afterDragOut
22469                  */
22470                 this.afterDragOut(target, e, id);
22471             }
22472         }
22473         this.cachedTarget = null;
22474     },
22475
22476     /**
22477      * An empty function by default, but provided so that you can perform a custom action before the dragged
22478      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22479      * @param {Roo.dd.DragDrop} target The drop target
22480      * @param {Event} e The event object
22481      * @param {String} id The id of the dragged element
22482      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22483      */
22484     beforeDragOut : function(target, e, id){
22485         return true;
22486     },
22487     
22488     // private
22489     onDragDrop : function(e, id){
22490         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22491         if(this.beforeDragDrop(target, e, id) !== false){
22492             if(target.isNotifyTarget){
22493                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22494                     this.onValidDrop(target, e, id);
22495                 }else{
22496                     this.onInvalidDrop(target, e, id);
22497                 }
22498             }else{
22499                 this.onValidDrop(target, e, id);
22500             }
22501             
22502             if(this.afterDragDrop){
22503                 /**
22504                  * An empty function by default, but provided so that you can perform a custom action
22505                  * after a valid drag drop has occurred by providing an implementation.
22506                  * @param {Roo.dd.DragDrop} target The drop target
22507                  * @param {Event} e The event object
22508                  * @param {String} id The id of the dropped element
22509                  * @method afterDragDrop
22510                  */
22511                 this.afterDragDrop(target, e, id);
22512             }
22513         }
22514         delete this.cachedTarget;
22515     },
22516
22517     /**
22518      * An empty function by default, but provided so that you can perform a custom action before the dragged
22519      * item is dropped onto the target and optionally cancel the onDragDrop.
22520      * @param {Roo.dd.DragDrop} target The drop target
22521      * @param {Event} e The event object
22522      * @param {String} id The id of the dragged element
22523      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22524      */
22525     beforeDragDrop : function(target, e, id){
22526         return true;
22527     },
22528
22529     // private
22530     onValidDrop : function(target, e, id){
22531         this.hideProxy();
22532         if(this.afterValidDrop){
22533             /**
22534              * An empty function by default, but provided so that you can perform a custom action
22535              * after a valid drop has occurred by providing an implementation.
22536              * @param {Object} target The target DD 
22537              * @param {Event} e The event object
22538              * @param {String} id The id of the dropped element
22539              * @method afterInvalidDrop
22540              */
22541             this.afterValidDrop(target, e, id);
22542         }
22543     },
22544
22545     // private
22546     getRepairXY : function(e, data){
22547         return this.el.getXY();  
22548     },
22549
22550     // private
22551     onInvalidDrop : function(target, e, id){
22552         this.beforeInvalidDrop(target, e, id);
22553         if(this.cachedTarget){
22554             if(this.cachedTarget.isNotifyTarget){
22555                 this.cachedTarget.notifyOut(this, e, this.dragData);
22556             }
22557             this.cacheTarget = null;
22558         }
22559         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22560
22561         if(this.afterInvalidDrop){
22562             /**
22563              * An empty function by default, but provided so that you can perform a custom action
22564              * after an invalid drop has occurred by providing an implementation.
22565              * @param {Event} e The event object
22566              * @param {String} id The id of the dropped element
22567              * @method afterInvalidDrop
22568              */
22569             this.afterInvalidDrop(e, id);
22570         }
22571     },
22572
22573     // private
22574     afterRepair : function(){
22575         if(Roo.enableFx){
22576             this.el.highlight(this.hlColor || "c3daf9");
22577         }
22578         this.dragging = false;
22579     },
22580
22581     /**
22582      * An empty function by default, but provided so that you can perform a custom action after an invalid
22583      * drop has occurred.
22584      * @param {Roo.dd.DragDrop} target The drop target
22585      * @param {Event} e The event object
22586      * @param {String} id The id of the dragged element
22587      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22588      */
22589     beforeInvalidDrop : function(target, e, id){
22590         return true;
22591     },
22592
22593     // private
22594     handleMouseDown : function(e){
22595         if(this.dragging) {
22596             return;
22597         }
22598         var data = this.getDragData(e);
22599         if(data && this.onBeforeDrag(data, e) !== false){
22600             this.dragData = data;
22601             this.proxy.stop();
22602             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22603         } 
22604     },
22605
22606     /**
22607      * An empty function by default, but provided so that you can perform a custom action before the initial
22608      * drag event begins and optionally cancel it.
22609      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22610      * @param {Event} e The event object
22611      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22612      */
22613     onBeforeDrag : function(data, e){
22614         return true;
22615     },
22616
22617     /**
22618      * An empty function by default, but provided so that you can perform a custom action once the initial
22619      * drag event has begun.  The drag cannot be canceled from this function.
22620      * @param {Number} x The x position of the click on the dragged object
22621      * @param {Number} y The y position of the click on the dragged object
22622      */
22623     onStartDrag : Roo.emptyFn,
22624
22625     // private - YUI override
22626     startDrag : function(x, y){
22627         this.proxy.reset();
22628         this.dragging = true;
22629         this.proxy.update("");
22630         this.onInitDrag(x, y);
22631         this.proxy.show();
22632     },
22633
22634     // private
22635     onInitDrag : function(x, y){
22636         var clone = this.el.dom.cloneNode(true);
22637         clone.id = Roo.id(); // prevent duplicate ids
22638         this.proxy.update(clone);
22639         this.onStartDrag(x, y);
22640         return true;
22641     },
22642
22643     /**
22644      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22645      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22646      */
22647     getProxy : function(){
22648         return this.proxy;  
22649     },
22650
22651     /**
22652      * Hides the drag source's {@link Roo.dd.StatusProxy}
22653      */
22654     hideProxy : function(){
22655         this.proxy.hide();  
22656         this.proxy.reset(true);
22657         this.dragging = false;
22658     },
22659
22660     // private
22661     triggerCacheRefresh : function(){
22662         Roo.dd.DDM.refreshCache(this.groups);
22663     },
22664
22665     // private - override to prevent hiding
22666     b4EndDrag: function(e) {
22667     },
22668
22669     // private - override to prevent moving
22670     endDrag : function(e){
22671         this.onEndDrag(this.dragData, e);
22672     },
22673
22674     // private
22675     onEndDrag : function(data, e){
22676     },
22677     
22678     // private - pin to cursor
22679     autoOffset : function(x, y) {
22680         this.setDelta(-12, -20);
22681     }    
22682 });/*
22683  * Based on:
22684  * Ext JS Library 1.1.1
22685  * Copyright(c) 2006-2007, Ext JS, LLC.
22686  *
22687  * Originally Released Under LGPL - original licence link has changed is not relivant.
22688  *
22689  * Fork - LGPL
22690  * <script type="text/javascript">
22691  */
22692
22693
22694 /**
22695  * @class Roo.dd.DropTarget
22696  * @extends Roo.dd.DDTarget
22697  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22698  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22699  * @constructor
22700  * @param {String/HTMLElement/Element} el The container element
22701  * @param {Object} config
22702  */
22703 Roo.dd.DropTarget = function(el, config){
22704     this.el = Roo.get(el);
22705     
22706     var listeners = false; ;
22707     if (config && config.listeners) {
22708         listeners= config.listeners;
22709         delete config.listeners;
22710     }
22711     Roo.apply(this, config);
22712     
22713     if(this.containerScroll){
22714         Roo.dd.ScrollManager.register(this.el);
22715     }
22716     this.addEvents( {
22717          /**
22718          * @scope Roo.dd.DropTarget
22719          */
22720          
22721          /**
22722          * @event enter
22723          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22724          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22725          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22726          * 
22727          * IMPORTANT : it should set  this.valid to true|false
22728          * 
22729          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22730          * @param {Event} e The event
22731          * @param {Object} data An object containing arbitrary data supplied by the drag source
22732          */
22733         "enter" : true,
22734         
22735          /**
22736          * @event over
22737          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22738          * This method will be called on every mouse movement while the drag source is over the drop target.
22739          * This default implementation simply returns the dropAllowed config value.
22740          * 
22741          * IMPORTANT : it should set  this.valid to true|false
22742          * 
22743          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22744          * @param {Event} e The event
22745          * @param {Object} data An object containing arbitrary data supplied by the drag source
22746          
22747          */
22748         "over" : true,
22749         /**
22750          * @event out
22751          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22752          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22753          * overClass (if any) from the drop element.
22754          * 
22755          * 
22756          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22757          * @param {Event} e The event
22758          * @param {Object} data An object containing arbitrary data supplied by the drag source
22759          */
22760          "out" : true,
22761          
22762         /**
22763          * @event drop
22764          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22765          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22766          * implementation that does something to process the drop event and returns true so that the drag source's
22767          * repair action does not run.
22768          * 
22769          * IMPORTANT : it should set this.success
22770          * 
22771          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22772          * @param {Event} e The event
22773          * @param {Object} data An object containing arbitrary data supplied by the drag source
22774         */
22775          "drop" : true
22776     });
22777             
22778      
22779     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22780         this.el.dom, 
22781         this.ddGroup || this.group,
22782         {
22783             isTarget: true,
22784             listeners : listeners || {} 
22785            
22786         
22787         }
22788     );
22789
22790 };
22791
22792 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22793     /**
22794      * @cfg {String} overClass
22795      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22796      */
22797      /**
22798      * @cfg {String} ddGroup
22799      * The drag drop group to handle drop events for
22800      */
22801      
22802     /**
22803      * @cfg {String} dropAllowed
22804      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22805      */
22806     dropAllowed : "x-dd-drop-ok",
22807     /**
22808      * @cfg {String} dropNotAllowed
22809      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22810      */
22811     dropNotAllowed : "x-dd-drop-nodrop",
22812     /**
22813      * @cfg {boolean} success
22814      * set this after drop listener.. 
22815      */
22816     success : false,
22817     /**
22818      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22819      * if the drop point is valid for over/enter..
22820      */
22821     valid : false,
22822     // private
22823     isTarget : true,
22824
22825     // private
22826     isNotifyTarget : true,
22827     
22828     /**
22829      * @hide
22830      */
22831     notifyEnter : function(dd, e, data)
22832     {
22833         this.valid = true;
22834         this.fireEvent('enter', dd, e, data);
22835         if(this.overClass){
22836             this.el.addClass(this.overClass);
22837         }
22838         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22839             this.valid ? this.dropAllowed : this.dropNotAllowed
22840         );
22841     },
22842
22843     /**
22844      * @hide
22845      */
22846     notifyOver : function(dd, e, data)
22847     {
22848         this.valid = true;
22849         this.fireEvent('over', dd, e, data);
22850         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22851             this.valid ? this.dropAllowed : this.dropNotAllowed
22852         );
22853     },
22854
22855     /**
22856      * @hide
22857      */
22858     notifyOut : function(dd, e, data)
22859     {
22860         this.fireEvent('out', dd, e, data);
22861         if(this.overClass){
22862             this.el.removeClass(this.overClass);
22863         }
22864     },
22865
22866     /**
22867      * @hide
22868      */
22869     notifyDrop : function(dd, e, data)
22870     {
22871         this.success = false;
22872         this.fireEvent('drop', dd, e, data);
22873         return this.success;
22874     }
22875 });/*
22876  * Based on:
22877  * Ext JS Library 1.1.1
22878  * Copyright(c) 2006-2007, Ext JS, LLC.
22879  *
22880  * Originally Released Under LGPL - original licence link has changed is not relivant.
22881  *
22882  * Fork - LGPL
22883  * <script type="text/javascript">
22884  */
22885
22886
22887 /**
22888  * @class Roo.dd.DragZone
22889  * @extends Roo.dd.DragSource
22890  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22891  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22892  * @constructor
22893  * @param {String/HTMLElement/Element} el The container element
22894  * @param {Object} config
22895  */
22896 Roo.dd.DragZone = function(el, config){
22897     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22898     if(this.containerScroll){
22899         Roo.dd.ScrollManager.register(this.el);
22900     }
22901 };
22902
22903 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22904     /**
22905      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22906      * for auto scrolling during drag operations.
22907      */
22908     /**
22909      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22910      * method after a failed drop (defaults to "c3daf9" - light blue)
22911      */
22912
22913     /**
22914      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22915      * for a valid target to drag based on the mouse down. Override this method
22916      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22917      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22918      * @param {EventObject} e The mouse down event
22919      * @return {Object} The dragData
22920      */
22921     getDragData : function(e){
22922         return Roo.dd.Registry.getHandleFromEvent(e);
22923     },
22924     
22925     /**
22926      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22927      * this.dragData.ddel
22928      * @param {Number} x The x position of the click on the dragged object
22929      * @param {Number} y The y position of the click on the dragged object
22930      * @return {Boolean} true to continue the drag, false to cancel
22931      */
22932     onInitDrag : function(x, y){
22933         this.proxy.update(this.dragData.ddel.cloneNode(true));
22934         this.onStartDrag(x, y);
22935         return true;
22936     },
22937     
22938     /**
22939      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22940      */
22941     afterRepair : function(){
22942         if(Roo.enableFx){
22943             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22944         }
22945         this.dragging = false;
22946     },
22947
22948     /**
22949      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22950      * the XY of this.dragData.ddel
22951      * @param {EventObject} e The mouse up event
22952      * @return {Array} The xy location (e.g. [100, 200])
22953      */
22954     getRepairXY : function(e){
22955         return Roo.Element.fly(this.dragData.ddel).getXY();  
22956     }
22957 });/*
22958  * Based on:
22959  * Ext JS Library 1.1.1
22960  * Copyright(c) 2006-2007, Ext JS, LLC.
22961  *
22962  * Originally Released Under LGPL - original licence link has changed is not relivant.
22963  *
22964  * Fork - LGPL
22965  * <script type="text/javascript">
22966  */
22967 /**
22968  * @class Roo.dd.DropZone
22969  * @extends Roo.dd.DropTarget
22970  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22971  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22972  * @constructor
22973  * @param {String/HTMLElement/Element} el The container element
22974  * @param {Object} config
22975  */
22976 Roo.dd.DropZone = function(el, config){
22977     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22978 };
22979
22980 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22981     /**
22982      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22983      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22984      * provide your own custom lookup.
22985      * @param {Event} e The event
22986      * @return {Object} data The custom data
22987      */
22988     getTargetFromEvent : function(e){
22989         return Roo.dd.Registry.getTargetFromEvent(e);
22990     },
22991
22992     /**
22993      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22994      * that it has registered.  This method has no default implementation and should be overridden to provide
22995      * node-specific processing if necessary.
22996      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22997      * {@link #getTargetFromEvent} for this node)
22998      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22999      * @param {Event} e The event
23000      * @param {Object} data An object containing arbitrary data supplied by the drag source
23001      */
23002     onNodeEnter : function(n, dd, e, data){
23003         
23004     },
23005
23006     /**
23007      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23008      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23009      * overridden to provide the proper feedback.
23010      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23011      * {@link #getTargetFromEvent} for this node)
23012      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23013      * @param {Event} e The event
23014      * @param {Object} data An object containing arbitrary data supplied by the drag source
23015      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23016      * underlying {@link Roo.dd.StatusProxy} can be updated
23017      */
23018     onNodeOver : function(n, dd, e, data){
23019         return this.dropAllowed;
23020     },
23021
23022     /**
23023      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23024      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23025      * node-specific processing if necessary.
23026      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23027      * {@link #getTargetFromEvent} for this node)
23028      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23029      * @param {Event} e The event
23030      * @param {Object} data An object containing arbitrary data supplied by the drag source
23031      */
23032     onNodeOut : function(n, dd, e, data){
23033         
23034     },
23035
23036     /**
23037      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23038      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23039      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23040      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23041      * {@link #getTargetFromEvent} for this node)
23042      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23043      * @param {Event} e The event
23044      * @param {Object} data An object containing arbitrary data supplied by the drag source
23045      * @return {Boolean} True if the drop was valid, else false
23046      */
23047     onNodeDrop : function(n, dd, e, data){
23048         return false;
23049     },
23050
23051     /**
23052      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23053      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23054      * it should be overridden to provide the proper feedback if necessary.
23055      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23056      * @param {Event} e The event
23057      * @param {Object} data An object containing arbitrary data supplied by the drag source
23058      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23059      * underlying {@link Roo.dd.StatusProxy} can be updated
23060      */
23061     onContainerOver : function(dd, e, data){
23062         return this.dropNotAllowed;
23063     },
23064
23065     /**
23066      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23067      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23068      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23069      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23070      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23071      * @param {Event} e The event
23072      * @param {Object} data An object containing arbitrary data supplied by the drag source
23073      * @return {Boolean} True if the drop was valid, else false
23074      */
23075     onContainerDrop : function(dd, e, data){
23076         return false;
23077     },
23078
23079     /**
23080      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23081      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23082      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23083      * you should override this method and provide a custom implementation.
23084      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23085      * @param {Event} e The event
23086      * @param {Object} data An object containing arbitrary data supplied by the drag source
23087      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23088      * underlying {@link Roo.dd.StatusProxy} can be updated
23089      */
23090     notifyEnter : function(dd, e, data){
23091         return this.dropNotAllowed;
23092     },
23093
23094     /**
23095      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23096      * This method will be called on every mouse movement while the drag source is over the drop zone.
23097      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23098      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23099      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23100      * registered node, it will call {@link #onContainerOver}.
23101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23102      * @param {Event} e The event
23103      * @param {Object} data An object containing arbitrary data supplied by the drag source
23104      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23105      * underlying {@link Roo.dd.StatusProxy} can be updated
23106      */
23107     notifyOver : function(dd, e, data){
23108         var n = this.getTargetFromEvent(e);
23109         if(!n){ // not over valid drop target
23110             if(this.lastOverNode){
23111                 this.onNodeOut(this.lastOverNode, dd, e, data);
23112                 this.lastOverNode = null;
23113             }
23114             return this.onContainerOver(dd, e, data);
23115         }
23116         if(this.lastOverNode != n){
23117             if(this.lastOverNode){
23118                 this.onNodeOut(this.lastOverNode, dd, e, data);
23119             }
23120             this.onNodeEnter(n, dd, e, data);
23121             this.lastOverNode = n;
23122         }
23123         return this.onNodeOver(n, dd, e, data);
23124     },
23125
23126     /**
23127      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23128      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23129      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23130      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23131      * @param {Event} e The event
23132      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23133      */
23134     notifyOut : function(dd, e, data){
23135         if(this.lastOverNode){
23136             this.onNodeOut(this.lastOverNode, dd, e, data);
23137             this.lastOverNode = null;
23138         }
23139     },
23140
23141     /**
23142      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23143      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23144      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23145      * otherwise it will call {@link #onContainerDrop}.
23146      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23147      * @param {Event} e The event
23148      * @param {Object} data An object containing arbitrary data supplied by the drag source
23149      * @return {Boolean} True if the drop was valid, else false
23150      */
23151     notifyDrop : function(dd, e, data){
23152         if(this.lastOverNode){
23153             this.onNodeOut(this.lastOverNode, dd, e, data);
23154             this.lastOverNode = null;
23155         }
23156         var n = this.getTargetFromEvent(e);
23157         return n ?
23158             this.onNodeDrop(n, dd, e, data) :
23159             this.onContainerDrop(dd, e, data);
23160     },
23161
23162     // private
23163     triggerCacheRefresh : function(){
23164         Roo.dd.DDM.refreshCache(this.groups);
23165     }  
23166 });/*
23167  * Based on:
23168  * Ext JS Library 1.1.1
23169  * Copyright(c) 2006-2007, Ext JS, LLC.
23170  *
23171  * Originally Released Under LGPL - original licence link has changed is not relivant.
23172  *
23173  * Fork - LGPL
23174  * <script type="text/javascript">
23175  */
23176
23177
23178 /**
23179  * @class Roo.data.SortTypes
23180  * @static
23181  * Defines the default sorting (casting?) comparison functions used when sorting data.
23182  */
23183 Roo.data.SortTypes = {
23184     /**
23185      * Default sort that does nothing
23186      * @param {Mixed} s The value being converted
23187      * @return {Mixed} The comparison value
23188      */
23189     none : function(s){
23190         return s;
23191     },
23192     
23193     /**
23194      * The regular expression used to strip tags
23195      * @type {RegExp}
23196      * @property
23197      */
23198     stripTagsRE : /<\/?[^>]+>/gi,
23199     
23200     /**
23201      * Strips all HTML tags to sort on text only
23202      * @param {Mixed} s The value being converted
23203      * @return {String} The comparison value
23204      */
23205     asText : function(s){
23206         return String(s).replace(this.stripTagsRE, "");
23207     },
23208     
23209     /**
23210      * Strips all HTML tags to sort on text only - Case insensitive
23211      * @param {Mixed} s The value being converted
23212      * @return {String} The comparison value
23213      */
23214     asUCText : function(s){
23215         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23216     },
23217     
23218     /**
23219      * Case insensitive string
23220      * @param {Mixed} s The value being converted
23221      * @return {String} The comparison value
23222      */
23223     asUCString : function(s) {
23224         return String(s).toUpperCase();
23225     },
23226     
23227     /**
23228      * Date sorting
23229      * @param {Mixed} s The value being converted
23230      * @return {Number} The comparison value
23231      */
23232     asDate : function(s) {
23233         if(!s){
23234             return 0;
23235         }
23236         if(s instanceof Date){
23237             return s.getTime();
23238         }
23239         return Date.parse(String(s));
23240     },
23241     
23242     /**
23243      * Float sorting
23244      * @param {Mixed} s The value being converted
23245      * @return {Float} The comparison value
23246      */
23247     asFloat : function(s) {
23248         var val = parseFloat(String(s).replace(/,/g, ""));
23249         if(isNaN(val)) {
23250             val = 0;
23251         }
23252         return val;
23253     },
23254     
23255     /**
23256      * Integer sorting
23257      * @param {Mixed} s The value being converted
23258      * @return {Number} The comparison value
23259      */
23260     asInt : function(s) {
23261         var val = parseInt(String(s).replace(/,/g, ""));
23262         if(isNaN(val)) {
23263             val = 0;
23264         }
23265         return val;
23266     }
23267 };/*
23268  * Based on:
23269  * Ext JS Library 1.1.1
23270  * Copyright(c) 2006-2007, Ext JS, LLC.
23271  *
23272  * Originally Released Under LGPL - original licence link has changed is not relivant.
23273  *
23274  * Fork - LGPL
23275  * <script type="text/javascript">
23276  */
23277
23278 /**
23279 * @class Roo.data.Record
23280  * Instances of this class encapsulate both record <em>definition</em> information, and record
23281  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23282  * to access Records cached in an {@link Roo.data.Store} object.<br>
23283  * <p>
23284  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23285  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23286  * objects.<br>
23287  * <p>
23288  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23289  * @constructor
23290  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23291  * {@link #create}. The parameters are the same.
23292  * @param {Array} data An associative Array of data values keyed by the field name.
23293  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23294  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23295  * not specified an integer id is generated.
23296  */
23297 Roo.data.Record = function(data, id){
23298     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23299     this.data = data;
23300 };
23301
23302 /**
23303  * Generate a constructor for a specific record layout.
23304  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23305  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23306  * Each field definition object may contain the following properties: <ul>
23307  * <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,
23308  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23309  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23310  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23311  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23312  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23313  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23314  * this may be omitted.</p></li>
23315  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23316  * <ul><li>auto (Default, implies no conversion)</li>
23317  * <li>string</li>
23318  * <li>int</li>
23319  * <li>float</li>
23320  * <li>boolean</li>
23321  * <li>date</li></ul></p></li>
23322  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23323  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23324  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23325  * by the Reader into an object that will be stored in the Record. It is passed the
23326  * following parameters:<ul>
23327  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23328  * </ul></p></li>
23329  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23330  * </ul>
23331  * <br>usage:<br><pre><code>
23332 var TopicRecord = Roo.data.Record.create(
23333     {name: 'title', mapping: 'topic_title'},
23334     {name: 'author', mapping: 'username'},
23335     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23336     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23337     {name: 'lastPoster', mapping: 'user2'},
23338     {name: 'excerpt', mapping: 'post_text'}
23339 );
23340
23341 var myNewRecord = new TopicRecord({
23342     title: 'Do my job please',
23343     author: 'noobie',
23344     totalPosts: 1,
23345     lastPost: new Date(),
23346     lastPoster: 'Animal',
23347     excerpt: 'No way dude!'
23348 });
23349 myStore.add(myNewRecord);
23350 </code></pre>
23351  * @method create
23352  * @static
23353  */
23354 Roo.data.Record.create = function(o){
23355     var f = function(){
23356         f.superclass.constructor.apply(this, arguments);
23357     };
23358     Roo.extend(f, Roo.data.Record);
23359     var p = f.prototype;
23360     p.fields = new Roo.util.MixedCollection(false, function(field){
23361         return field.name;
23362     });
23363     for(var i = 0, len = o.length; i < len; i++){
23364         p.fields.add(new Roo.data.Field(o[i]));
23365     }
23366     f.getField = function(name){
23367         return p.fields.get(name);  
23368     };
23369     return f;
23370 };
23371
23372 Roo.data.Record.AUTO_ID = 1000;
23373 Roo.data.Record.EDIT = 'edit';
23374 Roo.data.Record.REJECT = 'reject';
23375 Roo.data.Record.COMMIT = 'commit';
23376
23377 Roo.data.Record.prototype = {
23378     /**
23379      * Readonly flag - true if this record has been modified.
23380      * @type Boolean
23381      */
23382     dirty : false,
23383     editing : false,
23384     error: null,
23385     modified: null,
23386
23387     // private
23388     join : function(store){
23389         this.store = store;
23390     },
23391
23392     /**
23393      * Set the named field to the specified value.
23394      * @param {String} name The name of the field to set.
23395      * @param {Object} value The value to set the field to.
23396      */
23397     set : function(name, value){
23398         if(this.data[name] == value){
23399             return;
23400         }
23401         this.dirty = true;
23402         if(!this.modified){
23403             this.modified = {};
23404         }
23405         if(typeof this.modified[name] == 'undefined'){
23406             this.modified[name] = this.data[name];
23407         }
23408         this.data[name] = value;
23409         if(!this.editing && this.store){
23410             this.store.afterEdit(this);
23411         }       
23412     },
23413
23414     /**
23415      * Get the value of the named field.
23416      * @param {String} name The name of the field to get the value of.
23417      * @return {Object} The value of the field.
23418      */
23419     get : function(name){
23420         return this.data[name]; 
23421     },
23422
23423     // private
23424     beginEdit : function(){
23425         this.editing = true;
23426         this.modified = {}; 
23427     },
23428
23429     // private
23430     cancelEdit : function(){
23431         this.editing = false;
23432         delete this.modified;
23433     },
23434
23435     // private
23436     endEdit : function(){
23437         this.editing = false;
23438         if(this.dirty && this.store){
23439             this.store.afterEdit(this);
23440         }
23441     },
23442
23443     /**
23444      * Usually called by the {@link Roo.data.Store} which owns the Record.
23445      * Rejects all changes made to the Record since either creation, or the last commit operation.
23446      * Modified fields are reverted to their original values.
23447      * <p>
23448      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23449      * of reject operations.
23450      */
23451     reject : function(){
23452         var m = this.modified;
23453         for(var n in m){
23454             if(typeof m[n] != "function"){
23455                 this.data[n] = m[n];
23456             }
23457         }
23458         this.dirty = false;
23459         delete this.modified;
23460         this.editing = false;
23461         if(this.store){
23462             this.store.afterReject(this);
23463         }
23464     },
23465
23466     /**
23467      * Usually called by the {@link Roo.data.Store} which owns the Record.
23468      * Commits all changes made to the Record since either creation, or the last commit operation.
23469      * <p>
23470      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23471      * of commit operations.
23472      */
23473     commit : function(){
23474         this.dirty = false;
23475         delete this.modified;
23476         this.editing = false;
23477         if(this.store){
23478             this.store.afterCommit(this);
23479         }
23480     },
23481
23482     // private
23483     hasError : function(){
23484         return this.error != null;
23485     },
23486
23487     // private
23488     clearError : function(){
23489         this.error = null;
23490     },
23491
23492     /**
23493      * Creates a copy of this record.
23494      * @param {String} id (optional) A new record id if you don't want to use this record's id
23495      * @return {Record}
23496      */
23497     copy : function(newId) {
23498         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23499     }
23500 };/*
23501  * Based on:
23502  * Ext JS Library 1.1.1
23503  * Copyright(c) 2006-2007, Ext JS, LLC.
23504  *
23505  * Originally Released Under LGPL - original licence link has changed is not relivant.
23506  *
23507  * Fork - LGPL
23508  * <script type="text/javascript">
23509  */
23510
23511
23512
23513 /**
23514  * @class Roo.data.Store
23515  * @extends Roo.util.Observable
23516  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23517  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23518  * <p>
23519  * 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
23520  * has no knowledge of the format of the data returned by the Proxy.<br>
23521  * <p>
23522  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23523  * instances from the data object. These records are cached and made available through accessor functions.
23524  * @constructor
23525  * Creates a new Store.
23526  * @param {Object} config A config object containing the objects needed for the Store to access data,
23527  * and read the data into Records.
23528  */
23529 Roo.data.Store = function(config){
23530     this.data = new Roo.util.MixedCollection(false);
23531     this.data.getKey = function(o){
23532         return o.id;
23533     };
23534     this.baseParams = {};
23535     // private
23536     this.paramNames = {
23537         "start" : "start",
23538         "limit" : "limit",
23539         "sort" : "sort",
23540         "dir" : "dir",
23541         "multisort" : "_multisort"
23542     };
23543
23544     if(config && config.data){
23545         this.inlineData = config.data;
23546         delete config.data;
23547     }
23548
23549     Roo.apply(this, config);
23550     
23551     if(this.reader){ // reader passed
23552         this.reader = Roo.factory(this.reader, Roo.data);
23553         this.reader.xmodule = this.xmodule || false;
23554         if(!this.recordType){
23555             this.recordType = this.reader.recordType;
23556         }
23557         if(this.reader.onMetaChange){
23558             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23559         }
23560     }
23561
23562     if(this.recordType){
23563         this.fields = this.recordType.prototype.fields;
23564     }
23565     this.modified = [];
23566
23567     this.addEvents({
23568         /**
23569          * @event datachanged
23570          * Fires when the data cache has changed, and a widget which is using this Store
23571          * as a Record cache should refresh its view.
23572          * @param {Store} this
23573          */
23574         datachanged : true,
23575         /**
23576          * @event metachange
23577          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23578          * @param {Store} this
23579          * @param {Object} meta The JSON metadata
23580          */
23581         metachange : true,
23582         /**
23583          * @event add
23584          * Fires when Records have been added to the Store
23585          * @param {Store} this
23586          * @param {Roo.data.Record[]} records The array of Records added
23587          * @param {Number} index The index at which the record(s) were added
23588          */
23589         add : true,
23590         /**
23591          * @event remove
23592          * Fires when a Record has been removed from the Store
23593          * @param {Store} this
23594          * @param {Roo.data.Record} record The Record that was removed
23595          * @param {Number} index The index at which the record was removed
23596          */
23597         remove : true,
23598         /**
23599          * @event update
23600          * Fires when a Record has been updated
23601          * @param {Store} this
23602          * @param {Roo.data.Record} record The Record that was updated
23603          * @param {String} operation The update operation being performed.  Value may be one of:
23604          * <pre><code>
23605  Roo.data.Record.EDIT
23606  Roo.data.Record.REJECT
23607  Roo.data.Record.COMMIT
23608          * </code></pre>
23609          */
23610         update : true,
23611         /**
23612          * @event clear
23613          * Fires when the data cache has been cleared.
23614          * @param {Store} this
23615          */
23616         clear : true,
23617         /**
23618          * @event beforeload
23619          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23620          * the load action will be canceled.
23621          * @param {Store} this
23622          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23623          */
23624         beforeload : true,
23625         /**
23626          * @event beforeloadadd
23627          * Fires after a new set of Records has been loaded.
23628          * @param {Store} this
23629          * @param {Roo.data.Record[]} records The Records that were loaded
23630          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23631          */
23632         beforeloadadd : true,
23633         /**
23634          * @event load
23635          * Fires after a new set of Records has been loaded, before they are added to the store.
23636          * @param {Store} this
23637          * @param {Roo.data.Record[]} records The Records that were loaded
23638          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23639          * @params {Object} return from reader
23640          */
23641         load : true,
23642         /**
23643          * @event loadexception
23644          * Fires if an exception occurs in the Proxy during loading.
23645          * Called with the signature of the Proxy's "loadexception" event.
23646          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23647          * 
23648          * @param {Proxy} 
23649          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23650          * @param {Object} load options 
23651          * @param {Object} jsonData from your request (normally this contains the Exception)
23652          */
23653         loadexception : true
23654     });
23655     
23656     if(this.proxy){
23657         this.proxy = Roo.factory(this.proxy, Roo.data);
23658         this.proxy.xmodule = this.xmodule || false;
23659         this.relayEvents(this.proxy,  ["loadexception"]);
23660     }
23661     this.sortToggle = {};
23662     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23663
23664     Roo.data.Store.superclass.constructor.call(this);
23665
23666     if(this.inlineData){
23667         this.loadData(this.inlineData);
23668         delete this.inlineData;
23669     }
23670 };
23671
23672 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23673      /**
23674     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23675     * without a remote query - used by combo/forms at present.
23676     */
23677     
23678     /**
23679     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
23680     */
23681     /**
23682     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23683     */
23684     /**
23685     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
23686     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23687     */
23688     /**
23689     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23690     * on any HTTP request
23691     */
23692     /**
23693     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23694     */
23695     /**
23696     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23697     */
23698     multiSort: false,
23699     /**
23700     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23701     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23702     */
23703     remoteSort : false,
23704
23705     /**
23706     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23707      * loaded or when a record is removed. (defaults to false).
23708     */
23709     pruneModifiedRecords : false,
23710
23711     // private
23712     lastOptions : null,
23713
23714     /**
23715      * Add Records to the Store and fires the add event.
23716      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23717      */
23718     add : function(records){
23719         records = [].concat(records);
23720         for(var i = 0, len = records.length; i < len; i++){
23721             records[i].join(this);
23722         }
23723         var index = this.data.length;
23724         this.data.addAll(records);
23725         this.fireEvent("add", this, records, index);
23726     },
23727
23728     /**
23729      * Remove a Record from the Store and fires the remove event.
23730      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23731      */
23732     remove : function(record){
23733         var index = this.data.indexOf(record);
23734         this.data.removeAt(index);
23735  
23736         if(this.pruneModifiedRecords){
23737             this.modified.remove(record);
23738         }
23739         this.fireEvent("remove", this, record, index);
23740     },
23741
23742     /**
23743      * Remove all Records from the Store and fires the clear event.
23744      */
23745     removeAll : function(){
23746         this.data.clear();
23747         if(this.pruneModifiedRecords){
23748             this.modified = [];
23749         }
23750         this.fireEvent("clear", this);
23751     },
23752
23753     /**
23754      * Inserts Records to the Store at the given index and fires the add event.
23755      * @param {Number} index The start index at which to insert the passed Records.
23756      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23757      */
23758     insert : function(index, records){
23759         records = [].concat(records);
23760         for(var i = 0, len = records.length; i < len; i++){
23761             this.data.insert(index, records[i]);
23762             records[i].join(this);
23763         }
23764         this.fireEvent("add", this, records, index);
23765     },
23766
23767     /**
23768      * Get the index within the cache of the passed Record.
23769      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23770      * @return {Number} The index of the passed Record. Returns -1 if not found.
23771      */
23772     indexOf : function(record){
23773         return this.data.indexOf(record);
23774     },
23775
23776     /**
23777      * Get the index within the cache of the Record with the passed id.
23778      * @param {String} id The id of the Record to find.
23779      * @return {Number} The index of the Record. Returns -1 if not found.
23780      */
23781     indexOfId : function(id){
23782         return this.data.indexOfKey(id);
23783     },
23784
23785     /**
23786      * Get the Record with the specified id.
23787      * @param {String} id The id of the Record to find.
23788      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23789      */
23790     getById : function(id){
23791         return this.data.key(id);
23792     },
23793
23794     /**
23795      * Get the Record at the specified index.
23796      * @param {Number} index The index of the Record to find.
23797      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23798      */
23799     getAt : function(index){
23800         return this.data.itemAt(index);
23801     },
23802
23803     /**
23804      * Returns a range of Records between specified indices.
23805      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23806      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23807      * @return {Roo.data.Record[]} An array of Records
23808      */
23809     getRange : function(start, end){
23810         return this.data.getRange(start, end);
23811     },
23812
23813     // private
23814     storeOptions : function(o){
23815         o = Roo.apply({}, o);
23816         delete o.callback;
23817         delete o.scope;
23818         this.lastOptions = o;
23819     },
23820
23821     /**
23822      * Loads the Record cache from the configured Proxy using the configured Reader.
23823      * <p>
23824      * If using remote paging, then the first load call must specify the <em>start</em>
23825      * and <em>limit</em> properties in the options.params property to establish the initial
23826      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23827      * <p>
23828      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23829      * and this call will return before the new data has been loaded. Perform any post-processing
23830      * in a callback function, or in a "load" event handler.</strong>
23831      * <p>
23832      * @param {Object} options An object containing properties which control loading options:<ul>
23833      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23834      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23835      * passed the following arguments:<ul>
23836      * <li>r : Roo.data.Record[]</li>
23837      * <li>options: Options object from the load call</li>
23838      * <li>success: Boolean success indicator</li></ul></li>
23839      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23840      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23841      * </ul>
23842      */
23843     load : function(options){
23844         options = options || {};
23845         if(this.fireEvent("beforeload", this, options) !== false){
23846             this.storeOptions(options);
23847             var p = Roo.apply(options.params || {}, this.baseParams);
23848             // if meta was not loaded from remote source.. try requesting it.
23849             if (!this.reader.metaFromRemote) {
23850                 p._requestMeta = 1;
23851             }
23852             if(this.sortInfo && this.remoteSort){
23853                 var pn = this.paramNames;
23854                 p[pn["sort"]] = this.sortInfo.field;
23855                 p[pn["dir"]] = this.sortInfo.direction;
23856             }
23857             if (this.multiSort) {
23858                 var pn = this.paramNames;
23859                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23860             }
23861             
23862             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23863         }
23864     },
23865
23866     /**
23867      * Reloads the Record cache from the configured Proxy using the configured Reader and
23868      * the options from the last load operation performed.
23869      * @param {Object} options (optional) An object containing properties which may override the options
23870      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23871      * the most recently used options are reused).
23872      */
23873     reload : function(options){
23874         this.load(Roo.applyIf(options||{}, this.lastOptions));
23875     },
23876
23877     // private
23878     // Called as a callback by the Reader during a load operation.
23879     loadRecords : function(o, options, success){
23880          
23881         if(!o){
23882             if(success !== false){
23883                 this.fireEvent("load", this, [], options, o);
23884             }
23885             if(options.callback){
23886                 options.callback.call(options.scope || this, [], options, false);
23887             }
23888             return;
23889         }
23890         // if data returned failure - throw an exception.
23891         if (o.success === false) {
23892             // show a message if no listener is registered.
23893             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23894                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23895             }
23896             // loadmask wil be hooked into this..
23897             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23898             return;
23899         }
23900         var r = o.records, t = o.totalRecords || r.length;
23901         
23902         this.fireEvent("beforeloadadd", this, r, options, o);
23903         
23904         if(!options || options.add !== true){
23905             if(this.pruneModifiedRecords){
23906                 this.modified = [];
23907             }
23908             for(var i = 0, len = r.length; i < len; i++){
23909                 r[i].join(this);
23910             }
23911             if(this.snapshot){
23912                 this.data = this.snapshot;
23913                 delete this.snapshot;
23914             }
23915             this.data.clear();
23916             this.data.addAll(r);
23917             this.totalLength = t;
23918             this.applySort();
23919             this.fireEvent("datachanged", this);
23920         }else{
23921             this.totalLength = Math.max(t, this.data.length+r.length);
23922             this.add(r);
23923         }
23924         
23925         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23926                 
23927             var e = new Roo.data.Record({});
23928
23929             e.set(this.parent.displayField, this.parent.emptyTitle);
23930             e.set(this.parent.valueField, '');
23931
23932             this.insert(0, e);
23933         }
23934             
23935         this.fireEvent("load", this, r, options, o);
23936         if(options.callback){
23937             options.callback.call(options.scope || this, r, options, true);
23938         }
23939     },
23940
23941
23942     /**
23943      * Loads data from a passed data block. A Reader which understands the format of the data
23944      * must have been configured in the constructor.
23945      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23946      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23947      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23948      */
23949     loadData : function(o, append){
23950         var r = this.reader.readRecords(o);
23951         this.loadRecords(r, {add: append}, true);
23952     },
23953     
23954      /**
23955      * using 'cn' the nested child reader read the child array into it's child stores.
23956      * @param {Object} rec The record with a 'children array
23957      */
23958     loadDataFromChildren : function(rec)
23959     {
23960         this.loadData(this.reader.toLoadData(rec));
23961     },
23962     
23963
23964     /**
23965      * Gets the number of cached records.
23966      * <p>
23967      * <em>If using paging, this may not be the total size of the dataset. If the data object
23968      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23969      * the data set size</em>
23970      */
23971     getCount : function(){
23972         return this.data.length || 0;
23973     },
23974
23975     /**
23976      * Gets the total number of records in the dataset as returned by the server.
23977      * <p>
23978      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23979      * the dataset size</em>
23980      */
23981     getTotalCount : function(){
23982         return this.totalLength || 0;
23983     },
23984
23985     /**
23986      * Returns the sort state of the Store as an object with two properties:
23987      * <pre><code>
23988  field {String} The name of the field by which the Records are sorted
23989  direction {String} The sort order, "ASC" or "DESC"
23990      * </code></pre>
23991      */
23992     getSortState : function(){
23993         return this.sortInfo;
23994     },
23995
23996     // private
23997     applySort : function(){
23998         if(this.sortInfo && !this.remoteSort){
23999             var s = this.sortInfo, f = s.field;
24000             var st = this.fields.get(f).sortType;
24001             var fn = function(r1, r2){
24002                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24003                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24004             };
24005             this.data.sort(s.direction, fn);
24006             if(this.snapshot && this.snapshot != this.data){
24007                 this.snapshot.sort(s.direction, fn);
24008             }
24009         }
24010     },
24011
24012     /**
24013      * Sets the default sort column and order to be used by the next load operation.
24014      * @param {String} fieldName The name of the field to sort by.
24015      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24016      */
24017     setDefaultSort : function(field, dir){
24018         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24019     },
24020
24021     /**
24022      * Sort the Records.
24023      * If remote sorting is used, the sort is performed on the server, and the cache is
24024      * reloaded. If local sorting is used, the cache is sorted internally.
24025      * @param {String} fieldName The name of the field to sort by.
24026      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24027      */
24028     sort : function(fieldName, dir){
24029         var f = this.fields.get(fieldName);
24030         if(!dir){
24031             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24032             
24033             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24034                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24035             }else{
24036                 dir = f.sortDir;
24037             }
24038         }
24039         this.sortToggle[f.name] = dir;
24040         this.sortInfo = {field: f.name, direction: dir};
24041         if(!this.remoteSort){
24042             this.applySort();
24043             this.fireEvent("datachanged", this);
24044         }else{
24045             this.load(this.lastOptions);
24046         }
24047     },
24048
24049     /**
24050      * Calls the specified function for each of the Records in the cache.
24051      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24052      * Returning <em>false</em> aborts and exits the iteration.
24053      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24054      */
24055     each : function(fn, scope){
24056         this.data.each(fn, scope);
24057     },
24058
24059     /**
24060      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24061      * (e.g., during paging).
24062      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24063      */
24064     getModifiedRecords : function(){
24065         return this.modified;
24066     },
24067
24068     // private
24069     createFilterFn : function(property, value, anyMatch){
24070         if(!value.exec){ // not a regex
24071             value = String(value);
24072             if(value.length == 0){
24073                 return false;
24074             }
24075             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24076         }
24077         return function(r){
24078             return value.test(r.data[property]);
24079         };
24080     },
24081
24082     /**
24083      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24084      * @param {String} property A field on your records
24085      * @param {Number} start The record index to start at (defaults to 0)
24086      * @param {Number} end The last record index to include (defaults to length - 1)
24087      * @return {Number} The sum
24088      */
24089     sum : function(property, start, end){
24090         var rs = this.data.items, v = 0;
24091         start = start || 0;
24092         end = (end || end === 0) ? end : rs.length-1;
24093
24094         for(var i = start; i <= end; i++){
24095             v += (rs[i].data[property] || 0);
24096         }
24097         return v;
24098     },
24099
24100     /**
24101      * Filter the records by a specified property.
24102      * @param {String} field A field on your records
24103      * @param {String/RegExp} value Either a string that the field
24104      * should start with or a RegExp to test against the field
24105      * @param {Boolean} anyMatch True to match any part not just the beginning
24106      */
24107     filter : function(property, value, anyMatch){
24108         var fn = this.createFilterFn(property, value, anyMatch);
24109         return fn ? this.filterBy(fn) : this.clearFilter();
24110     },
24111
24112     /**
24113      * Filter by a function. The specified function will be called with each
24114      * record in this data source. If the function returns true the record is included,
24115      * otherwise it is filtered.
24116      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24117      * @param {Object} scope (optional) The scope of the function (defaults to this)
24118      */
24119     filterBy : function(fn, scope){
24120         this.snapshot = this.snapshot || this.data;
24121         this.data = this.queryBy(fn, scope||this);
24122         this.fireEvent("datachanged", this);
24123     },
24124
24125     /**
24126      * Query the records by a specified property.
24127      * @param {String} field A field on your records
24128      * @param {String/RegExp} value Either a string that the field
24129      * should start with or a RegExp to test against the field
24130      * @param {Boolean} anyMatch True to match any part not just the beginning
24131      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24132      */
24133     query : function(property, value, anyMatch){
24134         var fn = this.createFilterFn(property, value, anyMatch);
24135         return fn ? this.queryBy(fn) : this.data.clone();
24136     },
24137
24138     /**
24139      * Query by a function. The specified function will be called with each
24140      * record in this data source. If the function returns true the record is included
24141      * in the results.
24142      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24143      * @param {Object} scope (optional) The scope of the function (defaults to this)
24144       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24145      **/
24146     queryBy : function(fn, scope){
24147         var data = this.snapshot || this.data;
24148         return data.filterBy(fn, scope||this);
24149     },
24150
24151     /**
24152      * Collects unique values for a particular dataIndex from this store.
24153      * @param {String} dataIndex The property to collect
24154      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24155      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24156      * @return {Array} An array of the unique values
24157      **/
24158     collect : function(dataIndex, allowNull, bypassFilter){
24159         var d = (bypassFilter === true && this.snapshot) ?
24160                 this.snapshot.items : this.data.items;
24161         var v, sv, r = [], l = {};
24162         for(var i = 0, len = d.length; i < len; i++){
24163             v = d[i].data[dataIndex];
24164             sv = String(v);
24165             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24166                 l[sv] = true;
24167                 r[r.length] = v;
24168             }
24169         }
24170         return r;
24171     },
24172
24173     /**
24174      * Revert to a view of the Record cache with no filtering applied.
24175      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24176      */
24177     clearFilter : function(suppressEvent){
24178         if(this.snapshot && this.snapshot != this.data){
24179             this.data = this.snapshot;
24180             delete this.snapshot;
24181             if(suppressEvent !== true){
24182                 this.fireEvent("datachanged", this);
24183             }
24184         }
24185     },
24186
24187     // private
24188     afterEdit : function(record){
24189         if(this.modified.indexOf(record) == -1){
24190             this.modified.push(record);
24191         }
24192         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24193     },
24194     
24195     // private
24196     afterReject : function(record){
24197         this.modified.remove(record);
24198         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24199     },
24200
24201     // private
24202     afterCommit : function(record){
24203         this.modified.remove(record);
24204         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24205     },
24206
24207     /**
24208      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24209      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24210      */
24211     commitChanges : function(){
24212         var m = this.modified.slice(0);
24213         this.modified = [];
24214         for(var i = 0, len = m.length; i < len; i++){
24215             m[i].commit();
24216         }
24217     },
24218
24219     /**
24220      * Cancel outstanding changes on all changed records.
24221      */
24222     rejectChanges : function(){
24223         var m = this.modified.slice(0);
24224         this.modified = [];
24225         for(var i = 0, len = m.length; i < len; i++){
24226             m[i].reject();
24227         }
24228     },
24229
24230     onMetaChange : function(meta, rtype, o){
24231         this.recordType = rtype;
24232         this.fields = rtype.prototype.fields;
24233         delete this.snapshot;
24234         this.sortInfo = meta.sortInfo || this.sortInfo;
24235         this.modified = [];
24236         this.fireEvent('metachange', this, this.reader.meta);
24237     },
24238     
24239     moveIndex : function(data, type)
24240     {
24241         var index = this.indexOf(data);
24242         
24243         var newIndex = index + type;
24244         
24245         this.remove(data);
24246         
24247         this.insert(newIndex, data);
24248         
24249     }
24250 });/*
24251  * Based on:
24252  * Ext JS Library 1.1.1
24253  * Copyright(c) 2006-2007, Ext JS, LLC.
24254  *
24255  * Originally Released Under LGPL - original licence link has changed is not relivant.
24256  *
24257  * Fork - LGPL
24258  * <script type="text/javascript">
24259  */
24260
24261 /**
24262  * @class Roo.data.SimpleStore
24263  * @extends Roo.data.Store
24264  * Small helper class to make creating Stores from Array data easier.
24265  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24266  * @cfg {Array} fields An array of field definition objects, or field name strings.
24267  * @cfg {Object} an existing reader (eg. copied from another store)
24268  * @cfg {Array} data The multi-dimensional array of data
24269  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24270  * @cfg {Roo.data.Reader} reader  [not-required] 
24271  * @constructor
24272  * @param {Object} config
24273  */
24274 Roo.data.SimpleStore = function(config)
24275 {
24276     Roo.data.SimpleStore.superclass.constructor.call(this, {
24277         isLocal : true,
24278         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24279                 id: config.id
24280             },
24281             Roo.data.Record.create(config.fields)
24282         ),
24283         proxy : new Roo.data.MemoryProxy(config.data)
24284     });
24285     this.load();
24286 };
24287 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24288  * Based on:
24289  * Ext JS Library 1.1.1
24290  * Copyright(c) 2006-2007, Ext JS, LLC.
24291  *
24292  * Originally Released Under LGPL - original licence link has changed is not relivant.
24293  *
24294  * Fork - LGPL
24295  * <script type="text/javascript">
24296  */
24297
24298 /**
24299 /**
24300  * @extends Roo.data.Store
24301  * @class Roo.data.JsonStore
24302  * Small helper class to make creating Stores for JSON data easier. <br/>
24303 <pre><code>
24304 var store = new Roo.data.JsonStore({
24305     url: 'get-images.php',
24306     root: 'images',
24307     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24308 });
24309 </code></pre>
24310  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24311  * JsonReader and HttpProxy (unless inline data is provided).</b>
24312  * @cfg {Array} fields An array of field definition objects, or field name strings.
24313  * @constructor
24314  * @param {Object} config
24315  */
24316 Roo.data.JsonStore = function(c){
24317     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24318         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24319         reader: new Roo.data.JsonReader(c, c.fields)
24320     }));
24321 };
24322 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24323  * Based on:
24324  * Ext JS Library 1.1.1
24325  * Copyright(c) 2006-2007, Ext JS, LLC.
24326  *
24327  * Originally Released Under LGPL - original licence link has changed is not relivant.
24328  *
24329  * Fork - LGPL
24330  * <script type="text/javascript">
24331  */
24332
24333  
24334 Roo.data.Field = function(config){
24335     if(typeof config == "string"){
24336         config = {name: config};
24337     }
24338     Roo.apply(this, config);
24339     
24340     if(!this.type){
24341         this.type = "auto";
24342     }
24343     
24344     var st = Roo.data.SortTypes;
24345     // named sortTypes are supported, here we look them up
24346     if(typeof this.sortType == "string"){
24347         this.sortType = st[this.sortType];
24348     }
24349     
24350     // set default sortType for strings and dates
24351     if(!this.sortType){
24352         switch(this.type){
24353             case "string":
24354                 this.sortType = st.asUCString;
24355                 break;
24356             case "date":
24357                 this.sortType = st.asDate;
24358                 break;
24359             default:
24360                 this.sortType = st.none;
24361         }
24362     }
24363
24364     // define once
24365     var stripRe = /[\$,%]/g;
24366
24367     // prebuilt conversion function for this field, instead of
24368     // switching every time we're reading a value
24369     if(!this.convert){
24370         var cv, dateFormat = this.dateFormat;
24371         switch(this.type){
24372             case "":
24373             case "auto":
24374             case undefined:
24375                 cv = function(v){ return v; };
24376                 break;
24377             case "string":
24378                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24379                 break;
24380             case "int":
24381                 cv = function(v){
24382                     return v !== undefined && v !== null && v !== '' ?
24383                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24384                     };
24385                 break;
24386             case "float":
24387                 cv = function(v){
24388                     return v !== undefined && v !== null && v !== '' ?
24389                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24390                     };
24391                 break;
24392             case "bool":
24393             case "boolean":
24394                 cv = function(v){ return v === true || v === "true" || v == 1; };
24395                 break;
24396             case "date":
24397                 cv = function(v){
24398                     if(!v){
24399                         return '';
24400                     }
24401                     if(v instanceof Date){
24402                         return v;
24403                     }
24404                     if(dateFormat){
24405                         if(dateFormat == "timestamp"){
24406                             return new Date(v*1000);
24407                         }
24408                         return Date.parseDate(v, dateFormat);
24409                     }
24410                     var parsed = Date.parse(v);
24411                     return parsed ? new Date(parsed) : null;
24412                 };
24413              break;
24414             
24415         }
24416         this.convert = cv;
24417     }
24418 };
24419
24420 Roo.data.Field.prototype = {
24421     dateFormat: null,
24422     defaultValue: "",
24423     mapping: null,
24424     sortType : null,
24425     sortDir : "ASC"
24426 };/*
24427  * Based on:
24428  * Ext JS Library 1.1.1
24429  * Copyright(c) 2006-2007, Ext JS, LLC.
24430  *
24431  * Originally Released Under LGPL - original licence link has changed is not relivant.
24432  *
24433  * Fork - LGPL
24434  * <script type="text/javascript">
24435  */
24436  
24437 // Base class for reading structured data from a data source.  This class is intended to be
24438 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24439
24440 /**
24441  * @class Roo.data.DataReader
24442  * @abstract
24443  * Base class for reading structured data from a data source.  This class is intended to be
24444  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24445  */
24446
24447 Roo.data.DataReader = function(meta, recordType){
24448     
24449     this.meta = meta;
24450     
24451     this.recordType = recordType instanceof Array ? 
24452         Roo.data.Record.create(recordType) : recordType;
24453 };
24454
24455 Roo.data.DataReader.prototype = {
24456     
24457     
24458     readerType : 'Data',
24459      /**
24460      * Create an empty record
24461      * @param {Object} data (optional) - overlay some values
24462      * @return {Roo.data.Record} record created.
24463      */
24464     newRow :  function(d) {
24465         var da =  {};
24466         this.recordType.prototype.fields.each(function(c) {
24467             switch( c.type) {
24468                 case 'int' : da[c.name] = 0; break;
24469                 case 'date' : da[c.name] = new Date(); break;
24470                 case 'float' : da[c.name] = 0.0; break;
24471                 case 'boolean' : da[c.name] = false; break;
24472                 default : da[c.name] = ""; break;
24473             }
24474             
24475         });
24476         return new this.recordType(Roo.apply(da, d));
24477     }
24478     
24479     
24480 };/*
24481  * Based on:
24482  * Ext JS Library 1.1.1
24483  * Copyright(c) 2006-2007, Ext JS, LLC.
24484  *
24485  * Originally Released Under LGPL - original licence link has changed is not relivant.
24486  *
24487  * Fork - LGPL
24488  * <script type="text/javascript">
24489  */
24490
24491 /**
24492  * @class Roo.data.DataProxy
24493  * @extends Roo.util.Observable
24494  * @abstract
24495  * This class is an abstract base class for implementations which provide retrieval of
24496  * unformatted data objects.<br>
24497  * <p>
24498  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24499  * (of the appropriate type which knows how to parse the data object) to provide a block of
24500  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24501  * <p>
24502  * Custom implementations must implement the load method as described in
24503  * {@link Roo.data.HttpProxy#load}.
24504  */
24505 Roo.data.DataProxy = function(){
24506     this.addEvents({
24507         /**
24508          * @event beforeload
24509          * Fires before a network request is made to retrieve a data object.
24510          * @param {Object} This DataProxy object.
24511          * @param {Object} params The params parameter to the load function.
24512          */
24513         beforeload : true,
24514         /**
24515          * @event load
24516          * Fires before the load method's callback is called.
24517          * @param {Object} This DataProxy object.
24518          * @param {Object} o The data object.
24519          * @param {Object} arg The callback argument object passed to the load function.
24520          */
24521         load : true,
24522         /**
24523          * @event loadexception
24524          * Fires if an Exception occurs during data retrieval.
24525          * @param {Object} This DataProxy object.
24526          * @param {Object} o The data object.
24527          * @param {Object} arg The callback argument object passed to the load function.
24528          * @param {Object} e The Exception.
24529          */
24530         loadexception : true
24531     });
24532     Roo.data.DataProxy.superclass.constructor.call(this);
24533 };
24534
24535 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24536
24537     /**
24538      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24539      */
24540 /*
24541  * Based on:
24542  * Ext JS Library 1.1.1
24543  * Copyright(c) 2006-2007, Ext JS, LLC.
24544  *
24545  * Originally Released Under LGPL - original licence link has changed is not relivant.
24546  *
24547  * Fork - LGPL
24548  * <script type="text/javascript">
24549  */
24550 /**
24551  * @class Roo.data.MemoryProxy
24552  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24553  * to the Reader when its load method is called.
24554  * @constructor
24555  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24556  */
24557 Roo.data.MemoryProxy = function(data){
24558     if (data.data) {
24559         data = data.data;
24560     }
24561     Roo.data.MemoryProxy.superclass.constructor.call(this);
24562     this.data = data;
24563 };
24564
24565 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24566     
24567     /**
24568      * Load data from the requested source (in this case an in-memory
24569      * data object passed to the constructor), read the data object into
24570      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24571      * process that block using the passed callback.
24572      * @param {Object} params This parameter is not used by the MemoryProxy class.
24573      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24574      * object into a block of Roo.data.Records.
24575      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24576      * The function must be passed <ul>
24577      * <li>The Record block object</li>
24578      * <li>The "arg" argument from the load function</li>
24579      * <li>A boolean success indicator</li>
24580      * </ul>
24581      * @param {Object} scope The scope in which to call the callback
24582      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24583      */
24584     load : function(params, reader, callback, scope, arg){
24585         params = params || {};
24586         var result;
24587         try {
24588             result = reader.readRecords(params.data ? params.data :this.data);
24589         }catch(e){
24590             this.fireEvent("loadexception", this, arg, null, e);
24591             callback.call(scope, null, arg, false);
24592             return;
24593         }
24594         callback.call(scope, result, arg, true);
24595     },
24596     
24597     // private
24598     update : function(params, records){
24599         
24600     }
24601 });/*
24602  * Based on:
24603  * Ext JS Library 1.1.1
24604  * Copyright(c) 2006-2007, Ext JS, LLC.
24605  *
24606  * Originally Released Under LGPL - original licence link has changed is not relivant.
24607  *
24608  * Fork - LGPL
24609  * <script type="text/javascript">
24610  */
24611 /**
24612  * @class Roo.data.HttpProxy
24613  * @extends Roo.data.DataProxy
24614  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24615  * configured to reference a certain URL.<br><br>
24616  * <p>
24617  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24618  * from which the running page was served.<br><br>
24619  * <p>
24620  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24621  * <p>
24622  * Be aware that to enable the browser to parse an XML document, the server must set
24623  * the Content-Type header in the HTTP response to "text/xml".
24624  * @constructor
24625  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24626  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24627  * will be used to make the request.
24628  */
24629 Roo.data.HttpProxy = function(conn){
24630     Roo.data.HttpProxy.superclass.constructor.call(this);
24631     // is conn a conn config or a real conn?
24632     this.conn = conn;
24633     this.useAjax = !conn || !conn.events;
24634   
24635 };
24636
24637 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24638     // thse are take from connection...
24639     
24640     /**
24641      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24642      */
24643     /**
24644      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24645      * extra parameters to each request made by this object. (defaults to undefined)
24646      */
24647     /**
24648      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24649      *  to each request made by this object. (defaults to undefined)
24650      */
24651     /**
24652      * @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)
24653      */
24654     /**
24655      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24656      */
24657      /**
24658      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24659      * @type Boolean
24660      */
24661   
24662
24663     /**
24664      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24665      * @type Boolean
24666      */
24667     /**
24668      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24669      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24670      * a finer-grained basis than the DataProxy events.
24671      */
24672     getConnection : function(){
24673         return this.useAjax ? Roo.Ajax : this.conn;
24674     },
24675
24676     /**
24677      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24678      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24679      * process that block using the passed callback.
24680      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24681      * for the request to the remote server.
24682      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24683      * object into a block of Roo.data.Records.
24684      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24685      * The function must be passed <ul>
24686      * <li>The Record block object</li>
24687      * <li>The "arg" argument from the load function</li>
24688      * <li>A boolean success indicator</li>
24689      * </ul>
24690      * @param {Object} scope The scope in which to call the callback
24691      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24692      */
24693     load : function(params, reader, callback, scope, arg){
24694         if(this.fireEvent("beforeload", this, params) !== false){
24695             var  o = {
24696                 params : params || {},
24697                 request: {
24698                     callback : callback,
24699                     scope : scope,
24700                     arg : arg
24701                 },
24702                 reader: reader,
24703                 callback : this.loadResponse,
24704                 scope: this
24705             };
24706             if(this.useAjax){
24707                 Roo.applyIf(o, this.conn);
24708                 if(this.activeRequest){
24709                     Roo.Ajax.abort(this.activeRequest);
24710                 }
24711                 this.activeRequest = Roo.Ajax.request(o);
24712             }else{
24713                 this.conn.request(o);
24714             }
24715         }else{
24716             callback.call(scope||this, null, arg, false);
24717         }
24718     },
24719
24720     // private
24721     loadResponse : function(o, success, response){
24722         delete this.activeRequest;
24723         if(!success){
24724             this.fireEvent("loadexception", this, o, response);
24725             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24726             return;
24727         }
24728         var result;
24729         try {
24730             result = o.reader.read(response);
24731         }catch(e){
24732             this.fireEvent("loadexception", this, o, response, e);
24733             o.request.callback.call(o.request.scope, {
24734                     success : false,
24735                     raw : {
24736                         errorMsg : response.responseText
24737                     }
24738                     
24739                 }, o.request.arg, false);
24740             return;
24741         }
24742         
24743         this.fireEvent("load", this, o, o.request.arg);
24744         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24745     },
24746
24747     // private
24748     update : function(dataSet){
24749
24750     },
24751
24752     // private
24753     updateResponse : function(dataSet){
24754
24755     }
24756 });/*
24757  * Based on:
24758  * Ext JS Library 1.1.1
24759  * Copyright(c) 2006-2007, Ext JS, LLC.
24760  *
24761  * Originally Released Under LGPL - original licence link has changed is not relivant.
24762  *
24763  * Fork - LGPL
24764  * <script type="text/javascript">
24765  */
24766
24767 /**
24768  * @class Roo.data.ScriptTagProxy
24769  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24770  * other than the originating domain of the running page.<br><br>
24771  * <p>
24772  * <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
24773  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24774  * <p>
24775  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24776  * source code that is used as the source inside a &lt;script> tag.<br><br>
24777  * <p>
24778  * In order for the browser to process the returned data, the server must wrap the data object
24779  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24780  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24781  * depending on whether the callback name was passed:
24782  * <p>
24783  * <pre><code>
24784 boolean scriptTag = false;
24785 String cb = request.getParameter("callback");
24786 if (cb != null) {
24787     scriptTag = true;
24788     response.setContentType("text/javascript");
24789 } else {
24790     response.setContentType("application/x-json");
24791 }
24792 Writer out = response.getWriter();
24793 if (scriptTag) {
24794     out.write(cb + "(");
24795 }
24796 out.print(dataBlock.toJsonString());
24797 if (scriptTag) {
24798     out.write(");");
24799 }
24800 </pre></code>
24801  *
24802  * @constructor
24803  * @param {Object} config A configuration object.
24804  */
24805 Roo.data.ScriptTagProxy = function(config){
24806     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24807     Roo.apply(this, config);
24808     this.head = document.getElementsByTagName("head")[0];
24809 };
24810
24811 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24812
24813 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24814     /**
24815      * @cfg {String} url The URL from which to request the data object.
24816      */
24817     /**
24818      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24819      */
24820     timeout : 30000,
24821     /**
24822      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24823      * the server the name of the callback function set up by the load call to process the returned data object.
24824      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24825      * javascript output which calls this named function passing the data object as its only parameter.
24826      */
24827     callbackParam : "callback",
24828     /**
24829      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24830      * name to the request.
24831      */
24832     nocache : true,
24833
24834     /**
24835      * Load data from the configured URL, read the data object into
24836      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24837      * process that block using the passed callback.
24838      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24839      * for the request to the remote server.
24840      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24841      * object into a block of Roo.data.Records.
24842      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24843      * The function must be passed <ul>
24844      * <li>The Record block object</li>
24845      * <li>The "arg" argument from the load function</li>
24846      * <li>A boolean success indicator</li>
24847      * </ul>
24848      * @param {Object} scope The scope in which to call the callback
24849      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24850      */
24851     load : function(params, reader, callback, scope, arg){
24852         if(this.fireEvent("beforeload", this, params) !== false){
24853
24854             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24855
24856             var url = this.url;
24857             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24858             if(this.nocache){
24859                 url += "&_dc=" + (new Date().getTime());
24860             }
24861             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24862             var trans = {
24863                 id : transId,
24864                 cb : "stcCallback"+transId,
24865                 scriptId : "stcScript"+transId,
24866                 params : params,
24867                 arg : arg,
24868                 url : url,
24869                 callback : callback,
24870                 scope : scope,
24871                 reader : reader
24872             };
24873             var conn = this;
24874
24875             window[trans.cb] = function(o){
24876                 conn.handleResponse(o, trans);
24877             };
24878
24879             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24880
24881             if(this.autoAbort !== false){
24882                 this.abort();
24883             }
24884
24885             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24886
24887             var script = document.createElement("script");
24888             script.setAttribute("src", url);
24889             script.setAttribute("type", "text/javascript");
24890             script.setAttribute("id", trans.scriptId);
24891             this.head.appendChild(script);
24892
24893             this.trans = trans;
24894         }else{
24895             callback.call(scope||this, null, arg, false);
24896         }
24897     },
24898
24899     // private
24900     isLoading : function(){
24901         return this.trans ? true : false;
24902     },
24903
24904     /**
24905      * Abort the current server request.
24906      */
24907     abort : function(){
24908         if(this.isLoading()){
24909             this.destroyTrans(this.trans);
24910         }
24911     },
24912
24913     // private
24914     destroyTrans : function(trans, isLoaded){
24915         this.head.removeChild(document.getElementById(trans.scriptId));
24916         clearTimeout(trans.timeoutId);
24917         if(isLoaded){
24918             window[trans.cb] = undefined;
24919             try{
24920                 delete window[trans.cb];
24921             }catch(e){}
24922         }else{
24923             // if hasn't been loaded, wait for load to remove it to prevent script error
24924             window[trans.cb] = function(){
24925                 window[trans.cb] = undefined;
24926                 try{
24927                     delete window[trans.cb];
24928                 }catch(e){}
24929             };
24930         }
24931     },
24932
24933     // private
24934     handleResponse : function(o, trans){
24935         this.trans = false;
24936         this.destroyTrans(trans, true);
24937         var result;
24938         try {
24939             result = trans.reader.readRecords(o);
24940         }catch(e){
24941             this.fireEvent("loadexception", this, o, trans.arg, e);
24942             trans.callback.call(trans.scope||window, null, trans.arg, false);
24943             return;
24944         }
24945         this.fireEvent("load", this, o, trans.arg);
24946         trans.callback.call(trans.scope||window, result, trans.arg, true);
24947     },
24948
24949     // private
24950     handleFailure : function(trans){
24951         this.trans = false;
24952         this.destroyTrans(trans, false);
24953         this.fireEvent("loadexception", this, null, trans.arg);
24954         trans.callback.call(trans.scope||window, null, trans.arg, false);
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.JsonReader
24969  * @extends Roo.data.DataReader
24970  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24971  * based on mappings in a provided Roo.data.Record constructor.
24972  * 
24973  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24974  * in the reply previously. 
24975  * 
24976  * <p>
24977  * Example code:
24978  * <pre><code>
24979 var RecordDef = Roo.data.Record.create([
24980     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24981     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24982 ]);
24983 var myReader = new Roo.data.JsonReader({
24984     totalProperty: "results",    // The property which contains the total dataset size (optional)
24985     root: "rows",                // The property which contains an Array of row objects
24986     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24987 }, RecordDef);
24988 </code></pre>
24989  * <p>
24990  * This would consume a JSON file like this:
24991  * <pre><code>
24992 { 'results': 2, 'rows': [
24993     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24994     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24995 }
24996 </code></pre>
24997  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24998  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24999  * paged from the remote server.
25000  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25001  * @cfg {String} root name of the property which contains the Array of row objects.
25002  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25003  * @cfg {Array} fields Array of field definition objects
25004  * @constructor
25005  * Create a new JsonReader
25006  * @param {Object} meta Metadata configuration options
25007  * @param {Object} recordType Either an Array of field definition objects,
25008  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25009  */
25010 Roo.data.JsonReader = function(meta, recordType){
25011     
25012     meta = meta || {};
25013     // set some defaults:
25014     Roo.applyIf(meta, {
25015         totalProperty: 'total',
25016         successProperty : 'success',
25017         root : 'data',
25018         id : 'id'
25019     });
25020     
25021     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25022 };
25023 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25024     
25025     readerType : 'Json',
25026     
25027     /**
25028      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25029      * Used by Store query builder to append _requestMeta to params.
25030      * 
25031      */
25032     metaFromRemote : false,
25033     /**
25034      * This method is only used by a DataProxy which has retrieved data from a remote server.
25035      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25036      * @return {Object} data A data block which is used by an Roo.data.Store object as
25037      * a cache of Roo.data.Records.
25038      */
25039     read : function(response){
25040         var json = response.responseText;
25041        
25042         var o = /* eval:var:o */ eval("("+json+")");
25043         if(!o) {
25044             throw {message: "JsonReader.read: Json object not found"};
25045         }
25046         
25047         if(o.metaData){
25048             
25049             delete this.ef;
25050             this.metaFromRemote = true;
25051             this.meta = o.metaData;
25052             this.recordType = Roo.data.Record.create(o.metaData.fields);
25053             this.onMetaChange(this.meta, this.recordType, o);
25054         }
25055         return this.readRecords(o);
25056     },
25057
25058     // private function a store will implement
25059     onMetaChange : function(meta, recordType, o){
25060
25061     },
25062
25063     /**
25064          * @ignore
25065          */
25066     simpleAccess: function(obj, subsc) {
25067         return obj[subsc];
25068     },
25069
25070         /**
25071          * @ignore
25072          */
25073     getJsonAccessor: function(){
25074         var re = /[\[\.]/;
25075         return function(expr) {
25076             try {
25077                 return(re.test(expr))
25078                     ? new Function("obj", "return obj." + expr)
25079                     : function(obj){
25080                         return obj[expr];
25081                     };
25082             } catch(e){}
25083             return Roo.emptyFn;
25084         };
25085     }(),
25086
25087     /**
25088      * Create a data block containing Roo.data.Records from an XML document.
25089      * @param {Object} o An object which contains an Array of row objects in the property specified
25090      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25091      * which contains the total size of the dataset.
25092      * @return {Object} data A data block which is used by an Roo.data.Store object as
25093      * a cache of Roo.data.Records.
25094      */
25095     readRecords : function(o){
25096         /**
25097          * After any data loads, the raw JSON data is available for further custom processing.
25098          * @type Object
25099          */
25100         this.o = o;
25101         var s = this.meta, Record = this.recordType,
25102             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25103
25104 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25105         if (!this.ef) {
25106             if(s.totalProperty) {
25107                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25108                 }
25109                 if(s.successProperty) {
25110                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25111                 }
25112                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25113                 if (s.id) {
25114                         var g = this.getJsonAccessor(s.id);
25115                         this.getId = function(rec) {
25116                                 var r = g(rec);  
25117                                 return (r === undefined || r === "") ? null : r;
25118                         };
25119                 } else {
25120                         this.getId = function(){return null;};
25121                 }
25122             this.ef = [];
25123             for(var jj = 0; jj < fl; jj++){
25124                 f = fi[jj];
25125                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25126                 this.ef[jj] = this.getJsonAccessor(map);
25127             }
25128         }
25129
25130         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25131         if(s.totalProperty){
25132             var vt = parseInt(this.getTotal(o), 10);
25133             if(!isNaN(vt)){
25134                 totalRecords = vt;
25135             }
25136         }
25137         if(s.successProperty){
25138             var vs = this.getSuccess(o);
25139             if(vs === false || vs === 'false'){
25140                 success = false;
25141             }
25142         }
25143         var records = [];
25144         for(var i = 0; i < c; i++){
25145                 var n = root[i];
25146             var values = {};
25147             var id = this.getId(n);
25148             for(var j = 0; j < fl; j++){
25149                 f = fi[j];
25150             var v = this.ef[j](n);
25151             if (!f.convert) {
25152                 Roo.log('missing convert for ' + f.name);
25153                 Roo.log(f);
25154                 continue;
25155             }
25156             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25157             }
25158             var record = new Record(values, id);
25159             record.json = n;
25160             records[i] = record;
25161         }
25162         return {
25163             raw : o,
25164             success : success,
25165             records : records,
25166             totalRecords : totalRecords
25167         };
25168     },
25169     // used when loading children.. @see loadDataFromChildren
25170     toLoadData: function(rec)
25171     {
25172         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25173         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25174         return { data : data, total : data.length };
25175         
25176     }
25177 });/*
25178  * Based on:
25179  * Ext JS Library 1.1.1
25180  * Copyright(c) 2006-2007, Ext JS, LLC.
25181  *
25182  * Originally Released Under LGPL - original licence link has changed is not relivant.
25183  *
25184  * Fork - LGPL
25185  * <script type="text/javascript">
25186  */
25187
25188 /**
25189  * @class Roo.data.XmlReader
25190  * @extends Roo.data.DataReader
25191  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25192  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25193  * <p>
25194  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25195  * header in the HTTP response must be set to "text/xml".</em>
25196  * <p>
25197  * Example code:
25198  * <pre><code>
25199 var RecordDef = Roo.data.Record.create([
25200    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25201    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25202 ]);
25203 var myReader = new Roo.data.XmlReader({
25204    totalRecords: "results", // The element which contains the total dataset size (optional)
25205    record: "row",           // The repeated element which contains row information
25206    id: "id"                 // The element within the row that provides an ID for the record (optional)
25207 }, RecordDef);
25208 </code></pre>
25209  * <p>
25210  * This would consume an XML file like this:
25211  * <pre><code>
25212 &lt;?xml?>
25213 &lt;dataset>
25214  &lt;results>2&lt;/results>
25215  &lt;row>
25216    &lt;id>1&lt;/id>
25217    &lt;name>Bill&lt;/name>
25218    &lt;occupation>Gardener&lt;/occupation>
25219  &lt;/row>
25220  &lt;row>
25221    &lt;id>2&lt;/id>
25222    &lt;name>Ben&lt;/name>
25223    &lt;occupation>Horticulturalist&lt;/occupation>
25224  &lt;/row>
25225 &lt;/dataset>
25226 </code></pre>
25227  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25228  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25229  * paged from the remote server.
25230  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25231  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25232  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25233  * a record identifier value.
25234  * @constructor
25235  * Create a new XmlReader
25236  * @param {Object} meta Metadata configuration options
25237  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25238  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25239  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25240  */
25241 Roo.data.XmlReader = function(meta, recordType){
25242     meta = meta || {};
25243     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25244 };
25245 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25246     
25247     readerType : 'Xml',
25248     
25249     /**
25250      * This method is only used by a DataProxy which has retrieved data from a remote server.
25251          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25252          * to contain a method called 'responseXML' that returns an XML document object.
25253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25254      * a cache of Roo.data.Records.
25255      */
25256     read : function(response){
25257         var doc = response.responseXML;
25258         if(!doc) {
25259             throw {message: "XmlReader.read: XML Document not available"};
25260         }
25261         return this.readRecords(doc);
25262     },
25263
25264     /**
25265      * Create a data block containing Roo.data.Records from an XML document.
25266          * @param {Object} doc A parsed XML document.
25267      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25268      * a cache of Roo.data.Records.
25269      */
25270     readRecords : function(doc){
25271         /**
25272          * After any data loads/reads, the raw XML Document is available for further custom processing.
25273          * @type XMLDocument
25274          */
25275         this.xmlData = doc;
25276         var root = doc.documentElement || doc;
25277         var q = Roo.DomQuery;
25278         var recordType = this.recordType, fields = recordType.prototype.fields;
25279         var sid = this.meta.id;
25280         var totalRecords = 0, success = true;
25281         if(this.meta.totalRecords){
25282             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25283         }
25284         
25285         if(this.meta.success){
25286             var sv = q.selectValue(this.meta.success, root, true);
25287             success = sv !== false && sv !== 'false';
25288         }
25289         var records = [];
25290         var ns = q.select(this.meta.record, root);
25291         for(var i = 0, len = ns.length; i < len; i++) {
25292                 var n = ns[i];
25293                 var values = {};
25294                 var id = sid ? q.selectValue(sid, n) : undefined;
25295                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25296                     var f = fields.items[j];
25297                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25298                     v = f.convert(v);
25299                     values[f.name] = v;
25300                 }
25301                 var record = new recordType(values, id);
25302                 record.node = n;
25303                 records[records.length] = record;
25304             }
25305
25306             return {
25307                 success : success,
25308                 records : records,
25309                 totalRecords : totalRecords || records.length
25310             };
25311     }
25312 });/*
25313  * Based on:
25314  * Ext JS Library 1.1.1
25315  * Copyright(c) 2006-2007, Ext JS, LLC.
25316  *
25317  * Originally Released Under LGPL - original licence link has changed is not relivant.
25318  *
25319  * Fork - LGPL
25320  * <script type="text/javascript">
25321  */
25322
25323 /**
25324  * @class Roo.data.ArrayReader
25325  * @extends Roo.data.DataReader
25326  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25327  * Each element of that Array represents a row of data fields. The
25328  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25329  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25330  * <p>
25331  * Example code:.
25332  * <pre><code>
25333 var RecordDef = Roo.data.Record.create([
25334     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25335     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25336 ]);
25337 var myReader = new Roo.data.ArrayReader({
25338     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25339 }, RecordDef);
25340 </code></pre>
25341  * <p>
25342  * This would consume an Array like this:
25343  * <pre><code>
25344 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25345   </code></pre>
25346  
25347  * @constructor
25348  * Create a new JsonReader
25349  * @param {Object} meta Metadata configuration options.
25350  * @param {Object|Array} recordType Either an Array of field definition objects
25351  * 
25352  * @cfg {Array} fields Array of field definition objects
25353  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25354  * as specified to {@link Roo.data.Record#create},
25355  * or an {@link Roo.data.Record} object
25356  *
25357  * 
25358  * created using {@link Roo.data.Record#create}.
25359  */
25360 Roo.data.ArrayReader = function(meta, recordType)
25361 {    
25362     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25363 };
25364
25365 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25366     
25367       /**
25368      * Create a data block containing Roo.data.Records from an XML document.
25369      * @param {Object} o An Array of row objects which represents the dataset.
25370      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25371      * a cache of Roo.data.Records.
25372      */
25373     readRecords : function(o)
25374     {
25375         var sid = this.meta ? this.meta.id : null;
25376         var recordType = this.recordType, fields = recordType.prototype.fields;
25377         var records = [];
25378         var root = o;
25379         for(var i = 0; i < root.length; i++){
25380             var n = root[i];
25381             var values = {};
25382             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25383             for(var j = 0, jlen = fields.length; j < jlen; j++){
25384                 var f = fields.items[j];
25385                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25386                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25387                 v = f.convert(v);
25388                 values[f.name] = v;
25389             }
25390             var record = new recordType(values, id);
25391             record.json = n;
25392             records[records.length] = record;
25393         }
25394         return {
25395             records : records,
25396             totalRecords : records.length
25397         };
25398     },
25399     // used when loading children.. @see loadDataFromChildren
25400     toLoadData: function(rec)
25401     {
25402         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25403         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25404         
25405     }
25406     
25407     
25408 });/*
25409  * Based on:
25410  * Ext JS Library 1.1.1
25411  * Copyright(c) 2006-2007, Ext JS, LLC.
25412  *
25413  * Originally Released Under LGPL - original licence link has changed is not relivant.
25414  *
25415  * Fork - LGPL
25416  * <script type="text/javascript">
25417  */
25418
25419
25420 /**
25421  * @class Roo.data.Tree
25422  * @extends Roo.util.Observable
25423  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25424  * in the tree have most standard DOM functionality.
25425  * @constructor
25426  * @param {Node} root (optional) The root node
25427  */
25428 Roo.data.Tree = function(root){
25429    this.nodeHash = {};
25430    /**
25431     * The root node for this tree
25432     * @type Node
25433     */
25434    this.root = null;
25435    if(root){
25436        this.setRootNode(root);
25437    }
25438    this.addEvents({
25439        /**
25440         * @event append
25441         * Fires when a new child node is appended to a node in this tree.
25442         * @param {Tree} tree The owner tree
25443         * @param {Node} parent The parent node
25444         * @param {Node} node The newly appended node
25445         * @param {Number} index The index of the newly appended node
25446         */
25447        "append" : true,
25448        /**
25449         * @event remove
25450         * Fires when a child node is removed from a node in this tree.
25451         * @param {Tree} tree The owner tree
25452         * @param {Node} parent The parent node
25453         * @param {Node} node The child node removed
25454         */
25455        "remove" : true,
25456        /**
25457         * @event move
25458         * Fires when a node is moved to a new location in the tree
25459         * @param {Tree} tree The owner tree
25460         * @param {Node} node The node moved
25461         * @param {Node} oldParent The old parent of this node
25462         * @param {Node} newParent The new parent of this node
25463         * @param {Number} index The index it was moved to
25464         */
25465        "move" : true,
25466        /**
25467         * @event insert
25468         * Fires when a new child node is inserted in a node in this tree.
25469         * @param {Tree} tree The owner tree
25470         * @param {Node} parent The parent node
25471         * @param {Node} node The child node inserted
25472         * @param {Node} refNode The child node the node was inserted before
25473         */
25474        "insert" : true,
25475        /**
25476         * @event beforeappend
25477         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25478         * @param {Tree} tree The owner tree
25479         * @param {Node} parent The parent node
25480         * @param {Node} node The child node to be appended
25481         */
25482        "beforeappend" : true,
25483        /**
25484         * @event beforeremove
25485         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25486         * @param {Tree} tree The owner tree
25487         * @param {Node} parent The parent node
25488         * @param {Node} node The child node to be removed
25489         */
25490        "beforeremove" : true,
25491        /**
25492         * @event beforemove
25493         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25494         * @param {Tree} tree The owner tree
25495         * @param {Node} node The node being moved
25496         * @param {Node} oldParent The parent of the node
25497         * @param {Node} newParent The new parent the node is moving to
25498         * @param {Number} index The index it is being moved to
25499         */
25500        "beforemove" : true,
25501        /**
25502         * @event beforeinsert
25503         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25504         * @param {Tree} tree The owner tree
25505         * @param {Node} parent The parent node
25506         * @param {Node} node The child node to be inserted
25507         * @param {Node} refNode The child node the node is being inserted before
25508         */
25509        "beforeinsert" : true
25510    });
25511
25512     Roo.data.Tree.superclass.constructor.call(this);
25513 };
25514
25515 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25516     pathSeparator: "/",
25517
25518     proxyNodeEvent : function(){
25519         return this.fireEvent.apply(this, arguments);
25520     },
25521
25522     /**
25523      * Returns the root node for this tree.
25524      * @return {Node}
25525      */
25526     getRootNode : function(){
25527         return this.root;
25528     },
25529
25530     /**
25531      * Sets the root node for this tree.
25532      * @param {Node} node
25533      * @return {Node}
25534      */
25535     setRootNode : function(node){
25536         this.root = node;
25537         node.ownerTree = this;
25538         node.isRoot = true;
25539         this.registerNode(node);
25540         return node;
25541     },
25542
25543     /**
25544      * Gets a node in this tree by its id.
25545      * @param {String} id
25546      * @return {Node}
25547      */
25548     getNodeById : function(id){
25549         return this.nodeHash[id];
25550     },
25551
25552     registerNode : function(node){
25553         this.nodeHash[node.id] = node;
25554     },
25555
25556     unregisterNode : function(node){
25557         delete this.nodeHash[node.id];
25558     },
25559
25560     toString : function(){
25561         return "[Tree"+(this.id?" "+this.id:"")+"]";
25562     }
25563 });
25564
25565 /**
25566  * @class Roo.data.Node
25567  * @extends Roo.util.Observable
25568  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25569  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25570  * @constructor
25571  * @param {Object} attributes The attributes/config for the node
25572  */
25573 Roo.data.Node = function(attributes){
25574     /**
25575      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25576      * @type {Object}
25577      */
25578     this.attributes = attributes || {};
25579     this.leaf = this.attributes.leaf;
25580     /**
25581      * The node id. @type String
25582      */
25583     this.id = this.attributes.id;
25584     if(!this.id){
25585         this.id = Roo.id(null, "ynode-");
25586         this.attributes.id = this.id;
25587     }
25588      
25589     
25590     /**
25591      * All child nodes of this node. @type Array
25592      */
25593     this.childNodes = [];
25594     if(!this.childNodes.indexOf){ // indexOf is a must
25595         this.childNodes.indexOf = function(o){
25596             for(var i = 0, len = this.length; i < len; i++){
25597                 if(this[i] == o) {
25598                     return i;
25599                 }
25600             }
25601             return -1;
25602         };
25603     }
25604     /**
25605      * The parent node for this node. @type Node
25606      */
25607     this.parentNode = null;
25608     /**
25609      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25610      */
25611     this.firstChild = null;
25612     /**
25613      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25614      */
25615     this.lastChild = null;
25616     /**
25617      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25618      */
25619     this.previousSibling = null;
25620     /**
25621      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25622      */
25623     this.nextSibling = null;
25624
25625     this.addEvents({
25626        /**
25627         * @event append
25628         * Fires when a new child node is appended
25629         * @param {Tree} tree The owner tree
25630         * @param {Node} this This node
25631         * @param {Node} node The newly appended node
25632         * @param {Number} index The index of the newly appended node
25633         */
25634        "append" : true,
25635        /**
25636         * @event remove
25637         * Fires when a child node is removed
25638         * @param {Tree} tree The owner tree
25639         * @param {Node} this This node
25640         * @param {Node} node The removed node
25641         */
25642        "remove" : true,
25643        /**
25644         * @event move
25645         * Fires when this node is moved to a new location in the tree
25646         * @param {Tree} tree The owner tree
25647         * @param {Node} this This node
25648         * @param {Node} oldParent The old parent of this node
25649         * @param {Node} newParent The new parent of this node
25650         * @param {Number} index The index it was moved to
25651         */
25652        "move" : true,
25653        /**
25654         * @event insert
25655         * Fires when a new child node is inserted.
25656         * @param {Tree} tree The owner tree
25657         * @param {Node} this This node
25658         * @param {Node} node The child node inserted
25659         * @param {Node} refNode The child node the node was inserted before
25660         */
25661        "insert" : true,
25662        /**
25663         * @event beforeappend
25664         * Fires before a new child is appended, return false to cancel the append.
25665         * @param {Tree} tree The owner tree
25666         * @param {Node} this This node
25667         * @param {Node} node The child node to be appended
25668         */
25669        "beforeappend" : true,
25670        /**
25671         * @event beforeremove
25672         * Fires before a child is removed, return false to cancel the remove.
25673         * @param {Tree} tree The owner tree
25674         * @param {Node} this This node
25675         * @param {Node} node The child node to be removed
25676         */
25677        "beforeremove" : true,
25678        /**
25679         * @event beforemove
25680         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25681         * @param {Tree} tree The owner tree
25682         * @param {Node} this This node
25683         * @param {Node} oldParent The parent of this node
25684         * @param {Node} newParent The new parent this node is moving to
25685         * @param {Number} index The index it is being moved to
25686         */
25687        "beforemove" : true,
25688        /**
25689         * @event beforeinsert
25690         * Fires before a new child is inserted, return false to cancel the insert.
25691         * @param {Tree} tree The owner tree
25692         * @param {Node} this This node
25693         * @param {Node} node The child node to be inserted
25694         * @param {Node} refNode The child node the node is being inserted before
25695         */
25696        "beforeinsert" : true
25697    });
25698     this.listeners = this.attributes.listeners;
25699     Roo.data.Node.superclass.constructor.call(this);
25700 };
25701
25702 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25703     fireEvent : function(evtName){
25704         // first do standard event for this node
25705         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25706             return false;
25707         }
25708         // then bubble it up to the tree if the event wasn't cancelled
25709         var ot = this.getOwnerTree();
25710         if(ot){
25711             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25712                 return false;
25713             }
25714         }
25715         return true;
25716     },
25717
25718     /**
25719      * Returns true if this node is a leaf
25720      * @return {Boolean}
25721      */
25722     isLeaf : function(){
25723         return this.leaf === true;
25724     },
25725
25726     // private
25727     setFirstChild : function(node){
25728         this.firstChild = node;
25729     },
25730
25731     //private
25732     setLastChild : function(node){
25733         this.lastChild = node;
25734     },
25735
25736
25737     /**
25738      * Returns true if this node is the last child of its parent
25739      * @return {Boolean}
25740      */
25741     isLast : function(){
25742        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25743     },
25744
25745     /**
25746      * Returns true if this node is the first child of its parent
25747      * @return {Boolean}
25748      */
25749     isFirst : function(){
25750        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25751     },
25752
25753     hasChildNodes : function(){
25754         return !this.isLeaf() && this.childNodes.length > 0;
25755     },
25756
25757     /**
25758      * Insert node(s) as the last child node of this node.
25759      * @param {Node/Array} node The node or Array of nodes to append
25760      * @return {Node} The appended node if single append, or null if an array was passed
25761      */
25762     appendChild : function(node){
25763         var multi = false;
25764         if(node instanceof Array){
25765             multi = node;
25766         }else if(arguments.length > 1){
25767             multi = arguments;
25768         }
25769         
25770         // if passed an array or multiple args do them one by one
25771         if(multi){
25772             for(var i = 0, len = multi.length; i < len; i++) {
25773                 this.appendChild(multi[i]);
25774             }
25775         }else{
25776             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25777                 return false;
25778             }
25779             var index = this.childNodes.length;
25780             var oldParent = node.parentNode;
25781             // it's a move, make sure we move it cleanly
25782             if(oldParent){
25783                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25784                     return false;
25785                 }
25786                 oldParent.removeChild(node);
25787             }
25788             
25789             index = this.childNodes.length;
25790             if(index == 0){
25791                 this.setFirstChild(node);
25792             }
25793             this.childNodes.push(node);
25794             node.parentNode = this;
25795             var ps = this.childNodes[index-1];
25796             if(ps){
25797                 node.previousSibling = ps;
25798                 ps.nextSibling = node;
25799             }else{
25800                 node.previousSibling = null;
25801             }
25802             node.nextSibling = null;
25803             this.setLastChild(node);
25804             node.setOwnerTree(this.getOwnerTree());
25805             this.fireEvent("append", this.ownerTree, this, node, index);
25806             if(this.ownerTree) {
25807                 this.ownerTree.fireEvent("appendnode", this, node, index);
25808             }
25809             if(oldParent){
25810                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25811             }
25812             return node;
25813         }
25814     },
25815
25816     /**
25817      * Removes a child node from this node.
25818      * @param {Node} node The node to remove
25819      * @return {Node} The removed node
25820      */
25821     removeChild : function(node){
25822         var index = this.childNodes.indexOf(node);
25823         if(index == -1){
25824             return false;
25825         }
25826         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25827             return false;
25828         }
25829
25830         // remove it from childNodes collection
25831         this.childNodes.splice(index, 1);
25832
25833         // update siblings
25834         if(node.previousSibling){
25835             node.previousSibling.nextSibling = node.nextSibling;
25836         }
25837         if(node.nextSibling){
25838             node.nextSibling.previousSibling = node.previousSibling;
25839         }
25840
25841         // update child refs
25842         if(this.firstChild == node){
25843             this.setFirstChild(node.nextSibling);
25844         }
25845         if(this.lastChild == node){
25846             this.setLastChild(node.previousSibling);
25847         }
25848
25849         node.setOwnerTree(null);
25850         // clear any references from the node
25851         node.parentNode = null;
25852         node.previousSibling = null;
25853         node.nextSibling = null;
25854         this.fireEvent("remove", this.ownerTree, this, node);
25855         return node;
25856     },
25857
25858     /**
25859      * Inserts the first node before the second node in this nodes childNodes collection.
25860      * @param {Node} node The node to insert
25861      * @param {Node} refNode The node to insert before (if null the node is appended)
25862      * @return {Node} The inserted node
25863      */
25864     insertBefore : function(node, refNode){
25865         if(!refNode){ // like standard Dom, refNode can be null for append
25866             return this.appendChild(node);
25867         }
25868         // nothing to do
25869         if(node == refNode){
25870             return false;
25871         }
25872
25873         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25874             return false;
25875         }
25876         var index = this.childNodes.indexOf(refNode);
25877         var oldParent = node.parentNode;
25878         var refIndex = index;
25879
25880         // when moving internally, indexes will change after remove
25881         if(oldParent == this && this.childNodes.indexOf(node) < index){
25882             refIndex--;
25883         }
25884
25885         // it's a move, make sure we move it cleanly
25886         if(oldParent){
25887             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25888                 return false;
25889             }
25890             oldParent.removeChild(node);
25891         }
25892         if(refIndex == 0){
25893             this.setFirstChild(node);
25894         }
25895         this.childNodes.splice(refIndex, 0, node);
25896         node.parentNode = this;
25897         var ps = this.childNodes[refIndex-1];
25898         if(ps){
25899             node.previousSibling = ps;
25900             ps.nextSibling = node;
25901         }else{
25902             node.previousSibling = null;
25903         }
25904         node.nextSibling = refNode;
25905         refNode.previousSibling = node;
25906         node.setOwnerTree(this.getOwnerTree());
25907         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25908         if(oldParent){
25909             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25910         }
25911         return node;
25912     },
25913
25914     /**
25915      * Returns the child node at the specified index.
25916      * @param {Number} index
25917      * @return {Node}
25918      */
25919     item : function(index){
25920         return this.childNodes[index];
25921     },
25922
25923     /**
25924      * Replaces one child node in this node with another.
25925      * @param {Node} newChild The replacement node
25926      * @param {Node} oldChild The node to replace
25927      * @return {Node} The replaced node
25928      */
25929     replaceChild : function(newChild, oldChild){
25930         this.insertBefore(newChild, oldChild);
25931         this.removeChild(oldChild);
25932         return oldChild;
25933     },
25934
25935     /**
25936      * Returns the index of a child node
25937      * @param {Node} node
25938      * @return {Number} The index of the node or -1 if it was not found
25939      */
25940     indexOf : function(child){
25941         return this.childNodes.indexOf(child);
25942     },
25943
25944     /**
25945      * Returns the tree this node is in.
25946      * @return {Tree}
25947      */
25948     getOwnerTree : function(){
25949         // if it doesn't have one, look for one
25950         if(!this.ownerTree){
25951             var p = this;
25952             while(p){
25953                 if(p.ownerTree){
25954                     this.ownerTree = p.ownerTree;
25955                     break;
25956                 }
25957                 p = p.parentNode;
25958             }
25959         }
25960         return this.ownerTree;
25961     },
25962
25963     /**
25964      * Returns depth of this node (the root node has a depth of 0)
25965      * @return {Number}
25966      */
25967     getDepth : function(){
25968         var depth = 0;
25969         var p = this;
25970         while(p.parentNode){
25971             ++depth;
25972             p = p.parentNode;
25973         }
25974         return depth;
25975     },
25976
25977     // private
25978     setOwnerTree : function(tree){
25979         // if it's move, we need to update everyone
25980         if(tree != this.ownerTree){
25981             if(this.ownerTree){
25982                 this.ownerTree.unregisterNode(this);
25983             }
25984             this.ownerTree = tree;
25985             var cs = this.childNodes;
25986             for(var i = 0, len = cs.length; i < len; i++) {
25987                 cs[i].setOwnerTree(tree);
25988             }
25989             if(tree){
25990                 tree.registerNode(this);
25991             }
25992         }
25993     },
25994
25995     /**
25996      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25997      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25998      * @return {String} The path
25999      */
26000     getPath : function(attr){
26001         attr = attr || "id";
26002         var p = this.parentNode;
26003         var b = [this.attributes[attr]];
26004         while(p){
26005             b.unshift(p.attributes[attr]);
26006             p = p.parentNode;
26007         }
26008         var sep = this.getOwnerTree().pathSeparator;
26009         return sep + b.join(sep);
26010     },
26011
26012     /**
26013      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26014      * function call will be the scope provided or the current node. The arguments to the function
26015      * will be the args provided or the current node. If the function returns false at any point,
26016      * the bubble is stopped.
26017      * @param {Function} fn The function to call
26018      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26019      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26020      */
26021     bubble : function(fn, scope, args){
26022         var p = this;
26023         while(p){
26024             if(fn.call(scope || p, args || p) === false){
26025                 break;
26026             }
26027             p = p.parentNode;
26028         }
26029     },
26030
26031     /**
26032      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26033      * function call will be the scope provided or the current node. The arguments to the function
26034      * will be the args provided or the current node. If the function returns false at any point,
26035      * the cascade is stopped on that branch.
26036      * @param {Function} fn The function to call
26037      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26038      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26039      */
26040     cascade : function(fn, scope, args){
26041         if(fn.call(scope || this, args || this) !== false){
26042             var cs = this.childNodes;
26043             for(var i = 0, len = cs.length; i < len; i++) {
26044                 cs[i].cascade(fn, scope, args);
26045             }
26046         }
26047     },
26048
26049     /**
26050      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26051      * function call will be the scope provided or the current node. The arguments to the function
26052      * will be the args provided or the current node. If the function returns false at any point,
26053      * the iteration stops.
26054      * @param {Function} fn The function to call
26055      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26056      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26057      */
26058     eachChild : function(fn, scope, args){
26059         var cs = this.childNodes;
26060         for(var i = 0, len = cs.length; i < len; i++) {
26061                 if(fn.call(scope || this, args || cs[i]) === false){
26062                     break;
26063                 }
26064         }
26065     },
26066
26067     /**
26068      * Finds the first child that has the attribute with the specified value.
26069      * @param {String} attribute The attribute name
26070      * @param {Mixed} value The value to search for
26071      * @return {Node} The found child or null if none was found
26072      */
26073     findChild : function(attribute, value){
26074         var cs = this.childNodes;
26075         for(var i = 0, len = cs.length; i < len; i++) {
26076                 if(cs[i].attributes[attribute] == value){
26077                     return cs[i];
26078                 }
26079         }
26080         return null;
26081     },
26082
26083     /**
26084      * Finds the first child by a custom function. The child matches if the function passed
26085      * returns true.
26086      * @param {Function} fn
26087      * @param {Object} scope (optional)
26088      * @return {Node} The found child or null if none was found
26089      */
26090     findChildBy : function(fn, scope){
26091         var cs = this.childNodes;
26092         for(var i = 0, len = cs.length; i < len; i++) {
26093                 if(fn.call(scope||cs[i], cs[i]) === true){
26094                     return cs[i];
26095                 }
26096         }
26097         return null;
26098     },
26099
26100     /**
26101      * Sorts this nodes children using the supplied sort function
26102      * @param {Function} fn
26103      * @param {Object} scope (optional)
26104      */
26105     sort : function(fn, scope){
26106         var cs = this.childNodes;
26107         var len = cs.length;
26108         if(len > 0){
26109             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26110             cs.sort(sortFn);
26111             for(var i = 0; i < len; i++){
26112                 var n = cs[i];
26113                 n.previousSibling = cs[i-1];
26114                 n.nextSibling = cs[i+1];
26115                 if(i == 0){
26116                     this.setFirstChild(n);
26117                 }
26118                 if(i == len-1){
26119                     this.setLastChild(n);
26120                 }
26121             }
26122         }
26123     },
26124
26125     /**
26126      * Returns true if this node is an ancestor (at any point) of the passed node.
26127      * @param {Node} node
26128      * @return {Boolean}
26129      */
26130     contains : function(node){
26131         return node.isAncestor(this);
26132     },
26133
26134     /**
26135      * Returns true if the passed node is an ancestor (at any point) of this node.
26136      * @param {Node} node
26137      * @return {Boolean}
26138      */
26139     isAncestor : function(node){
26140         var p = this.parentNode;
26141         while(p){
26142             if(p == node){
26143                 return true;
26144             }
26145             p = p.parentNode;
26146         }
26147         return false;
26148     },
26149
26150     toString : function(){
26151         return "[Node"+(this.id?" "+this.id:"")+"]";
26152     }
26153 });/*
26154  * Based on:
26155  * Ext JS Library 1.1.1
26156  * Copyright(c) 2006-2007, Ext JS, LLC.
26157  *
26158  * Originally Released Under LGPL - original licence link has changed is not relivant.
26159  *
26160  * Fork - LGPL
26161  * <script type="text/javascript">
26162  */
26163
26164
26165 /**
26166  * @class Roo.Shadow
26167  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26168  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26169  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26170  * @constructor
26171  * Create a new Shadow
26172  * @param {Object} config The config object
26173  */
26174 Roo.Shadow = function(config){
26175     Roo.apply(this, config);
26176     if(typeof this.mode != "string"){
26177         this.mode = this.defaultMode;
26178     }
26179     var o = this.offset, a = {h: 0};
26180     var rad = Math.floor(this.offset/2);
26181     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26182         case "drop":
26183             a.w = 0;
26184             a.l = a.t = o;
26185             a.t -= 1;
26186             if(Roo.isIE){
26187                 a.l -= this.offset + rad;
26188                 a.t -= this.offset + rad;
26189                 a.w -= rad;
26190                 a.h -= rad;
26191                 a.t += 1;
26192             }
26193         break;
26194         case "sides":
26195             a.w = (o*2);
26196             a.l = -o;
26197             a.t = o-1;
26198             if(Roo.isIE){
26199                 a.l -= (this.offset - rad);
26200                 a.t -= this.offset + rad;
26201                 a.l += 1;
26202                 a.w -= (this.offset - rad)*2;
26203                 a.w -= rad + 1;
26204                 a.h -= 1;
26205             }
26206         break;
26207         case "frame":
26208             a.w = a.h = (o*2);
26209             a.l = a.t = -o;
26210             a.t += 1;
26211             a.h -= 2;
26212             if(Roo.isIE){
26213                 a.l -= (this.offset - rad);
26214                 a.t -= (this.offset - rad);
26215                 a.l += 1;
26216                 a.w -= (this.offset + rad + 1);
26217                 a.h -= (this.offset + rad);
26218                 a.h += 1;
26219             }
26220         break;
26221     };
26222
26223     this.adjusts = a;
26224 };
26225
26226 Roo.Shadow.prototype = {
26227     /**
26228      * @cfg {String} mode
26229      * The shadow display mode.  Supports the following options:<br />
26230      * sides: Shadow displays on both sides and bottom only<br />
26231      * frame: Shadow displays equally on all four sides<br />
26232      * drop: Traditional bottom-right drop shadow (default)
26233      */
26234     mode: false,
26235     /**
26236      * @cfg {String} offset
26237      * The number of pixels to offset the shadow from the element (defaults to 4)
26238      */
26239     offset: 4,
26240
26241     // private
26242     defaultMode: "drop",
26243
26244     /**
26245      * Displays the shadow under the target element
26246      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26247      */
26248     show : function(target){
26249         target = Roo.get(target);
26250         if(!this.el){
26251             this.el = Roo.Shadow.Pool.pull();
26252             if(this.el.dom.nextSibling != target.dom){
26253                 this.el.insertBefore(target);
26254             }
26255         }
26256         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26257         if(Roo.isIE){
26258             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26259         }
26260         this.realign(
26261             target.getLeft(true),
26262             target.getTop(true),
26263             target.getWidth(),
26264             target.getHeight()
26265         );
26266         this.el.dom.style.display = "block";
26267     },
26268
26269     /**
26270      * Returns true if the shadow is visible, else false
26271      */
26272     isVisible : function(){
26273         return this.el ? true : false;  
26274     },
26275
26276     /**
26277      * Direct alignment when values are already available. Show must be called at least once before
26278      * calling this method to ensure it is initialized.
26279      * @param {Number} left The target element left position
26280      * @param {Number} top The target element top position
26281      * @param {Number} width The target element width
26282      * @param {Number} height The target element height
26283      */
26284     realign : function(l, t, w, h){
26285         if(!this.el){
26286             return;
26287         }
26288         var a = this.adjusts, d = this.el.dom, s = d.style;
26289         var iea = 0;
26290         s.left = (l+a.l)+"px";
26291         s.top = (t+a.t)+"px";
26292         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26293  
26294         if(s.width != sws || s.height != shs){
26295             s.width = sws;
26296             s.height = shs;
26297             if(!Roo.isIE){
26298                 var cn = d.childNodes;
26299                 var sww = Math.max(0, (sw-12))+"px";
26300                 cn[0].childNodes[1].style.width = sww;
26301                 cn[1].childNodes[1].style.width = sww;
26302                 cn[2].childNodes[1].style.width = sww;
26303                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26304             }
26305         }
26306     },
26307
26308     /**
26309      * Hides this shadow
26310      */
26311     hide : function(){
26312         if(this.el){
26313             this.el.dom.style.display = "none";
26314             Roo.Shadow.Pool.push(this.el);
26315             delete this.el;
26316         }
26317     },
26318
26319     /**
26320      * Adjust the z-index of this shadow
26321      * @param {Number} zindex The new z-index
26322      */
26323     setZIndex : function(z){
26324         this.zIndex = z;
26325         if(this.el){
26326             this.el.setStyle("z-index", z);
26327         }
26328     }
26329 };
26330
26331 // Private utility class that manages the internal Shadow cache
26332 Roo.Shadow.Pool = function(){
26333     var p = [];
26334     var markup = Roo.isIE ?
26335                  '<div class="x-ie-shadow"></div>' :
26336                  '<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>';
26337     return {
26338         pull : function(){
26339             var sh = p.shift();
26340             if(!sh){
26341                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26342                 sh.autoBoxAdjust = false;
26343             }
26344             return sh;
26345         },
26346
26347         push : function(sh){
26348             p.push(sh);
26349         }
26350     };
26351 }();/*
26352  * Based on:
26353  * Ext JS Library 1.1.1
26354  * Copyright(c) 2006-2007, Ext JS, LLC.
26355  *
26356  * Originally Released Under LGPL - original licence link has changed is not relivant.
26357  *
26358  * Fork - LGPL
26359  * <script type="text/javascript">
26360  */
26361
26362
26363 /**
26364  * @class Roo.SplitBar
26365  * @extends Roo.util.Observable
26366  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26367  * <br><br>
26368  * Usage:
26369  * <pre><code>
26370 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26371                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26372 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26373 split.minSize = 100;
26374 split.maxSize = 600;
26375 split.animate = true;
26376 split.on('moved', splitterMoved);
26377 </code></pre>
26378  * @constructor
26379  * Create a new SplitBar
26380  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26381  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26382  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26383  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26384                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26385                         position of the SplitBar).
26386  */
26387 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26388     
26389     /** @private */
26390     this.el = Roo.get(dragElement, true);
26391     this.el.dom.unselectable = "on";
26392     /** @private */
26393     this.resizingEl = Roo.get(resizingElement, true);
26394
26395     /**
26396      * @private
26397      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26398      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26399      * @type Number
26400      */
26401     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26402     
26403     /**
26404      * The minimum size of the resizing element. (Defaults to 0)
26405      * @type Number
26406      */
26407     this.minSize = 0;
26408     
26409     /**
26410      * The maximum size of the resizing element. (Defaults to 2000)
26411      * @type Number
26412      */
26413     this.maxSize = 2000;
26414     
26415     /**
26416      * Whether to animate the transition to the new size
26417      * @type Boolean
26418      */
26419     this.animate = false;
26420     
26421     /**
26422      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26423      * @type Boolean
26424      */
26425     this.useShim = false;
26426     
26427     /** @private */
26428     this.shim = null;
26429     
26430     if(!existingProxy){
26431         /** @private */
26432         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26433     }else{
26434         this.proxy = Roo.get(existingProxy).dom;
26435     }
26436     /** @private */
26437     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26438     
26439     /** @private */
26440     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26441     
26442     /** @private */
26443     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26444     
26445     /** @private */
26446     this.dragSpecs = {};
26447     
26448     /**
26449      * @private The adapter to use to positon and resize elements
26450      */
26451     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26452     this.adapter.init(this);
26453     
26454     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26455         /** @private */
26456         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26457         this.el.addClass("x-splitbar-h");
26458     }else{
26459         /** @private */
26460         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26461         this.el.addClass("x-splitbar-v");
26462     }
26463     
26464     this.addEvents({
26465         /**
26466          * @event resize
26467          * Fires when the splitter is moved (alias for {@link #event-moved})
26468          * @param {Roo.SplitBar} this
26469          * @param {Number} newSize the new width or height
26470          */
26471         "resize" : true,
26472         /**
26473          * @event moved
26474          * Fires when the splitter is moved
26475          * @param {Roo.SplitBar} this
26476          * @param {Number} newSize the new width or height
26477          */
26478         "moved" : true,
26479         /**
26480          * @event beforeresize
26481          * Fires before the splitter is dragged
26482          * @param {Roo.SplitBar} this
26483          */
26484         "beforeresize" : true,
26485
26486         "beforeapply" : true
26487     });
26488
26489     Roo.util.Observable.call(this);
26490 };
26491
26492 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26493     onStartProxyDrag : function(x, y){
26494         this.fireEvent("beforeresize", this);
26495         if(!this.overlay){
26496             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26497             o.unselectable();
26498             o.enableDisplayMode("block");
26499             // all splitbars share the same overlay
26500             Roo.SplitBar.prototype.overlay = o;
26501         }
26502         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26503         this.overlay.show();
26504         Roo.get(this.proxy).setDisplayed("block");
26505         var size = this.adapter.getElementSize(this);
26506         this.activeMinSize = this.getMinimumSize();;
26507         this.activeMaxSize = this.getMaximumSize();;
26508         var c1 = size - this.activeMinSize;
26509         var c2 = Math.max(this.activeMaxSize - size, 0);
26510         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26511             this.dd.resetConstraints();
26512             this.dd.setXConstraint(
26513                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26514                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26515             );
26516             this.dd.setYConstraint(0, 0);
26517         }else{
26518             this.dd.resetConstraints();
26519             this.dd.setXConstraint(0, 0);
26520             this.dd.setYConstraint(
26521                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26522                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26523             );
26524          }
26525         this.dragSpecs.startSize = size;
26526         this.dragSpecs.startPoint = [x, y];
26527         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26528     },
26529     
26530     /** 
26531      * @private Called after the drag operation by the DDProxy
26532      */
26533     onEndProxyDrag : function(e){
26534         Roo.get(this.proxy).setDisplayed(false);
26535         var endPoint = Roo.lib.Event.getXY(e);
26536         if(this.overlay){
26537             this.overlay.hide();
26538         }
26539         var newSize;
26540         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26541             newSize = this.dragSpecs.startSize + 
26542                 (this.placement == Roo.SplitBar.LEFT ?
26543                     endPoint[0] - this.dragSpecs.startPoint[0] :
26544                     this.dragSpecs.startPoint[0] - endPoint[0]
26545                 );
26546         }else{
26547             newSize = this.dragSpecs.startSize + 
26548                 (this.placement == Roo.SplitBar.TOP ?
26549                     endPoint[1] - this.dragSpecs.startPoint[1] :
26550                     this.dragSpecs.startPoint[1] - endPoint[1]
26551                 );
26552         }
26553         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26554         if(newSize != this.dragSpecs.startSize){
26555             if(this.fireEvent('beforeapply', this, newSize) !== false){
26556                 this.adapter.setElementSize(this, newSize);
26557                 this.fireEvent("moved", this, newSize);
26558                 this.fireEvent("resize", this, newSize);
26559             }
26560         }
26561     },
26562     
26563     /**
26564      * Get the adapter this SplitBar uses
26565      * @return The adapter object
26566      */
26567     getAdapter : function(){
26568         return this.adapter;
26569     },
26570     
26571     /**
26572      * Set the adapter this SplitBar uses
26573      * @param {Object} adapter A SplitBar adapter object
26574      */
26575     setAdapter : function(adapter){
26576         this.adapter = adapter;
26577         this.adapter.init(this);
26578     },
26579     
26580     /**
26581      * Gets the minimum size for the resizing element
26582      * @return {Number} The minimum size
26583      */
26584     getMinimumSize : function(){
26585         return this.minSize;
26586     },
26587     
26588     /**
26589      * Sets the minimum size for the resizing element
26590      * @param {Number} minSize The minimum size
26591      */
26592     setMinimumSize : function(minSize){
26593         this.minSize = minSize;
26594     },
26595     
26596     /**
26597      * Gets the maximum size for the resizing element
26598      * @return {Number} The maximum size
26599      */
26600     getMaximumSize : function(){
26601         return this.maxSize;
26602     },
26603     
26604     /**
26605      * Sets the maximum size for the resizing element
26606      * @param {Number} maxSize The maximum size
26607      */
26608     setMaximumSize : function(maxSize){
26609         this.maxSize = maxSize;
26610     },
26611     
26612     /**
26613      * Sets the initialize size for the resizing element
26614      * @param {Number} size The initial size
26615      */
26616     setCurrentSize : function(size){
26617         var oldAnimate = this.animate;
26618         this.animate = false;
26619         this.adapter.setElementSize(this, size);
26620         this.animate = oldAnimate;
26621     },
26622     
26623     /**
26624      * Destroy this splitbar. 
26625      * @param {Boolean} removeEl True to remove the element
26626      */
26627     destroy : function(removeEl){
26628         if(this.shim){
26629             this.shim.remove();
26630         }
26631         this.dd.unreg();
26632         this.proxy.parentNode.removeChild(this.proxy);
26633         if(removeEl){
26634             this.el.remove();
26635         }
26636     }
26637 });
26638
26639 /**
26640  * @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.
26641  */
26642 Roo.SplitBar.createProxy = function(dir){
26643     var proxy = new Roo.Element(document.createElement("div"));
26644     proxy.unselectable();
26645     var cls = 'x-splitbar-proxy';
26646     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26647     document.body.appendChild(proxy.dom);
26648     return proxy.dom;
26649 };
26650
26651 /** 
26652  * @class Roo.SplitBar.BasicLayoutAdapter
26653  * Default Adapter. It assumes the splitter and resizing element are not positioned
26654  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26655  */
26656 Roo.SplitBar.BasicLayoutAdapter = function(){
26657 };
26658
26659 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26660     // do nothing for now
26661     init : function(s){
26662     
26663     },
26664     /**
26665      * Called before drag operations to get the current size of the resizing element. 
26666      * @param {Roo.SplitBar} s The SplitBar using this adapter
26667      */
26668      getElementSize : function(s){
26669         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26670             return s.resizingEl.getWidth();
26671         }else{
26672             return s.resizingEl.getHeight();
26673         }
26674     },
26675     
26676     /**
26677      * Called after drag operations to set the size of the resizing element.
26678      * @param {Roo.SplitBar} s The SplitBar using this adapter
26679      * @param {Number} newSize The new size to set
26680      * @param {Function} onComplete A function to be invoked when resizing is complete
26681      */
26682     setElementSize : function(s, newSize, onComplete){
26683         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26684             if(!s.animate){
26685                 s.resizingEl.setWidth(newSize);
26686                 if(onComplete){
26687                     onComplete(s, newSize);
26688                 }
26689             }else{
26690                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26691             }
26692         }else{
26693             
26694             if(!s.animate){
26695                 s.resizingEl.setHeight(newSize);
26696                 if(onComplete){
26697                     onComplete(s, newSize);
26698                 }
26699             }else{
26700                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26701             }
26702         }
26703     }
26704 };
26705
26706 /** 
26707  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26708  * @extends Roo.SplitBar.BasicLayoutAdapter
26709  * Adapter that  moves the splitter element to align with the resized sizing element. 
26710  * Used with an absolute positioned SplitBar.
26711  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26712  * document.body, make sure you assign an id to the body element.
26713  */
26714 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26715     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26716     this.container = Roo.get(container);
26717 };
26718
26719 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26720     init : function(s){
26721         this.basic.init(s);
26722     },
26723     
26724     getElementSize : function(s){
26725         return this.basic.getElementSize(s);
26726     },
26727     
26728     setElementSize : function(s, newSize, onComplete){
26729         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26730     },
26731     
26732     moveSplitter : function(s){
26733         var yes = Roo.SplitBar;
26734         switch(s.placement){
26735             case yes.LEFT:
26736                 s.el.setX(s.resizingEl.getRight());
26737                 break;
26738             case yes.RIGHT:
26739                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26740                 break;
26741             case yes.TOP:
26742                 s.el.setY(s.resizingEl.getBottom());
26743                 break;
26744             case yes.BOTTOM:
26745                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26746                 break;
26747         }
26748     }
26749 };
26750
26751 /**
26752  * Orientation constant - Create a vertical SplitBar
26753  * @static
26754  * @type Number
26755  */
26756 Roo.SplitBar.VERTICAL = 1;
26757
26758 /**
26759  * Orientation constant - Create a horizontal SplitBar
26760  * @static
26761  * @type Number
26762  */
26763 Roo.SplitBar.HORIZONTAL = 2;
26764
26765 /**
26766  * Placement constant - The resizing element is to the left of the splitter element
26767  * @static
26768  * @type Number
26769  */
26770 Roo.SplitBar.LEFT = 1;
26771
26772 /**
26773  * Placement constant - The resizing element is to the right of the splitter element
26774  * @static
26775  * @type Number
26776  */
26777 Roo.SplitBar.RIGHT = 2;
26778
26779 /**
26780  * Placement constant - The resizing element is positioned above the splitter element
26781  * @static
26782  * @type Number
26783  */
26784 Roo.SplitBar.TOP = 3;
26785
26786 /**
26787  * Placement constant - The resizing element is positioned under splitter element
26788  * @static
26789  * @type Number
26790  */
26791 Roo.SplitBar.BOTTOM = 4;
26792 /*
26793  * Based on:
26794  * Ext JS Library 1.1.1
26795  * Copyright(c) 2006-2007, Ext JS, LLC.
26796  *
26797  * Originally Released Under LGPL - original licence link has changed is not relivant.
26798  *
26799  * Fork - LGPL
26800  * <script type="text/javascript">
26801  */
26802
26803 /**
26804  * @class Roo.View
26805  * @extends Roo.util.Observable
26806  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26807  * This class also supports single and multi selection modes. <br>
26808  * Create a data model bound view:
26809  <pre><code>
26810  var store = new Roo.data.Store(...);
26811
26812  var view = new Roo.View({
26813     el : "my-element",
26814     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26815  
26816     singleSelect: true,
26817     selectedClass: "ydataview-selected",
26818     store: store
26819  });
26820
26821  // listen for node click?
26822  view.on("click", function(vw, index, node, e){
26823  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26824  });
26825
26826  // load XML data
26827  dataModel.load("foobar.xml");
26828  </code></pre>
26829  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26830  * <br><br>
26831  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26832  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26833  * 
26834  * Note: old style constructor is still suported (container, template, config)
26835  * 
26836  * @constructor
26837  * Create a new View
26838  * @param {Object} config The config object
26839  * 
26840  */
26841 Roo.View = function(config, depreciated_tpl, depreciated_config){
26842     
26843     this.parent = false;
26844     
26845     if (typeof(depreciated_tpl) == 'undefined') {
26846         // new way.. - universal constructor.
26847         Roo.apply(this, config);
26848         this.el  = Roo.get(this.el);
26849     } else {
26850         // old format..
26851         this.el  = Roo.get(config);
26852         this.tpl = depreciated_tpl;
26853         Roo.apply(this, depreciated_config);
26854     }
26855     this.wrapEl  = this.el.wrap().wrap();
26856     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26857     
26858     
26859     if(typeof(this.tpl) == "string"){
26860         this.tpl = new Roo.Template(this.tpl);
26861     } else {
26862         // support xtype ctors..
26863         this.tpl = new Roo.factory(this.tpl, Roo);
26864     }
26865     
26866     
26867     this.tpl.compile();
26868     
26869     /** @private */
26870     this.addEvents({
26871         /**
26872          * @event beforeclick
26873          * Fires before a click is processed. Returns false to cancel the default action.
26874          * @param {Roo.View} this
26875          * @param {Number} index The index of the target node
26876          * @param {HTMLElement} node The target node
26877          * @param {Roo.EventObject} e The raw event object
26878          */
26879             "beforeclick" : true,
26880         /**
26881          * @event click
26882          * Fires when a template node is clicked.
26883          * @param {Roo.View} this
26884          * @param {Number} index The index of the target node
26885          * @param {HTMLElement} node The target node
26886          * @param {Roo.EventObject} e The raw event object
26887          */
26888             "click" : true,
26889         /**
26890          * @event dblclick
26891          * Fires when a template node is double clicked.
26892          * @param {Roo.View} this
26893          * @param {Number} index The index of the target node
26894          * @param {HTMLElement} node The target node
26895          * @param {Roo.EventObject} e The raw event object
26896          */
26897             "dblclick" : true,
26898         /**
26899          * @event contextmenu
26900          * Fires when a template node is right clicked.
26901          * @param {Roo.View} this
26902          * @param {Number} index The index of the target node
26903          * @param {HTMLElement} node The target node
26904          * @param {Roo.EventObject} e The raw event object
26905          */
26906             "contextmenu" : true,
26907         /**
26908          * @event selectionchange
26909          * Fires when the selected nodes change.
26910          * @param {Roo.View} this
26911          * @param {Array} selections Array of the selected nodes
26912          */
26913             "selectionchange" : true,
26914     
26915         /**
26916          * @event beforeselect
26917          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26918          * @param {Roo.View} this
26919          * @param {HTMLElement} node The node to be selected
26920          * @param {Array} selections Array of currently selected nodes
26921          */
26922             "beforeselect" : true,
26923         /**
26924          * @event preparedata
26925          * Fires on every row to render, to allow you to change the data.
26926          * @param {Roo.View} this
26927          * @param {Object} data to be rendered (change this)
26928          */
26929           "preparedata" : true
26930           
26931           
26932         });
26933
26934
26935
26936     this.el.on({
26937         "click": this.onClick,
26938         "dblclick": this.onDblClick,
26939         "contextmenu": this.onContextMenu,
26940         scope:this
26941     });
26942
26943     this.selections = [];
26944     this.nodes = [];
26945     this.cmp = new Roo.CompositeElementLite([]);
26946     if(this.store){
26947         this.store = Roo.factory(this.store, Roo.data);
26948         this.setStore(this.store, true);
26949     }
26950     
26951     if ( this.footer && this.footer.xtype) {
26952            
26953          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26954         
26955         this.footer.dataSource = this.store;
26956         this.footer.container = fctr;
26957         this.footer = Roo.factory(this.footer, Roo);
26958         fctr.insertFirst(this.el);
26959         
26960         // this is a bit insane - as the paging toolbar seems to detach the el..
26961 //        dom.parentNode.parentNode.parentNode
26962          // they get detached?
26963     }
26964     
26965     
26966     Roo.View.superclass.constructor.call(this);
26967     
26968     
26969 };
26970
26971 Roo.extend(Roo.View, Roo.util.Observable, {
26972     
26973      /**
26974      * @cfg {Roo.data.Store} store Data store to load data from.
26975      */
26976     store : false,
26977     
26978     /**
26979      * @cfg {String|Roo.Element} el The container element.
26980      */
26981     el : '',
26982     
26983     /**
26984      * @cfg {String|Roo.Template} tpl The template used by this View 
26985      */
26986     tpl : false,
26987     /**
26988      * @cfg {String} dataName the named area of the template to use as the data area
26989      *                          Works with domtemplates roo-name="name"
26990      */
26991     dataName: false,
26992     /**
26993      * @cfg {String} selectedClass The css class to add to selected nodes
26994      */
26995     selectedClass : "x-view-selected",
26996      /**
26997      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26998      */
26999     emptyText : "",
27000     
27001     /**
27002      * @cfg {String} text to display on mask (default Loading)
27003      */
27004     mask : false,
27005     /**
27006      * @cfg {Boolean} multiSelect Allow multiple selection
27007      */
27008     multiSelect : false,
27009     /**
27010      * @cfg {Boolean} singleSelect Allow single selection
27011      */
27012     singleSelect:  false,
27013     
27014     /**
27015      * @cfg {Boolean} toggleSelect - selecting 
27016      */
27017     toggleSelect : false,
27018     
27019     /**
27020      * @cfg {Boolean} tickable - selecting 
27021      */
27022     tickable : false,
27023     
27024     /**
27025      * Returns the element this view is bound to.
27026      * @return {Roo.Element}
27027      */
27028     getEl : function(){
27029         return this.wrapEl;
27030     },
27031     
27032     
27033
27034     /**
27035      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27036      */
27037     refresh : function(){
27038         //Roo.log('refresh');
27039         var t = this.tpl;
27040         
27041         // if we are using something like 'domtemplate', then
27042         // the what gets used is:
27043         // t.applySubtemplate(NAME, data, wrapping data..)
27044         // the outer template then get' applied with
27045         //     the store 'extra data'
27046         // and the body get's added to the
27047         //      roo-name="data" node?
27048         //      <span class='roo-tpl-{name}'></span> ?????
27049         
27050         
27051         
27052         this.clearSelections();
27053         this.el.update("");
27054         var html = [];
27055         var records = this.store.getRange();
27056         if(records.length < 1) {
27057             
27058             // is this valid??  = should it render a template??
27059             
27060             this.el.update(this.emptyText);
27061             return;
27062         }
27063         var el = this.el;
27064         if (this.dataName) {
27065             this.el.update(t.apply(this.store.meta)); //????
27066             el = this.el.child('.roo-tpl-' + this.dataName);
27067         }
27068         
27069         for(var i = 0, len = records.length; i < len; i++){
27070             var data = this.prepareData(records[i].data, i, records[i]);
27071             this.fireEvent("preparedata", this, data, i, records[i]);
27072             
27073             var d = Roo.apply({}, data);
27074             
27075             if(this.tickable){
27076                 Roo.apply(d, {'roo-id' : Roo.id()});
27077                 
27078                 var _this = this;
27079             
27080                 Roo.each(this.parent.item, function(item){
27081                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27082                         return;
27083                     }
27084                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27085                 });
27086             }
27087             
27088             html[html.length] = Roo.util.Format.trim(
27089                 this.dataName ?
27090                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27091                     t.apply(d)
27092             );
27093         }
27094         
27095         
27096         
27097         el.update(html.join(""));
27098         this.nodes = el.dom.childNodes;
27099         this.updateIndexes(0);
27100     },
27101     
27102
27103     /**
27104      * Function to override to reformat the data that is sent to
27105      * the template for each node.
27106      * DEPRICATED - use the preparedata event handler.
27107      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27108      * a JSON object for an UpdateManager bound view).
27109      */
27110     prepareData : function(data, index, record)
27111     {
27112         this.fireEvent("preparedata", this, data, index, record);
27113         return data;
27114     },
27115
27116     onUpdate : function(ds, record){
27117         // Roo.log('on update');   
27118         this.clearSelections();
27119         var index = this.store.indexOf(record);
27120         var n = this.nodes[index];
27121         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27122         n.parentNode.removeChild(n);
27123         this.updateIndexes(index, index);
27124     },
27125
27126     
27127     
27128 // --------- FIXME     
27129     onAdd : function(ds, records, index)
27130     {
27131         //Roo.log(['on Add', ds, records, index] );        
27132         this.clearSelections();
27133         if(this.nodes.length == 0){
27134             this.refresh();
27135             return;
27136         }
27137         var n = this.nodes[index];
27138         for(var i = 0, len = records.length; i < len; i++){
27139             var d = this.prepareData(records[i].data, i, records[i]);
27140             if(n){
27141                 this.tpl.insertBefore(n, d);
27142             }else{
27143                 
27144                 this.tpl.append(this.el, d);
27145             }
27146         }
27147         this.updateIndexes(index);
27148     },
27149
27150     onRemove : function(ds, record, index){
27151        // Roo.log('onRemove');
27152         this.clearSelections();
27153         var el = this.dataName  ?
27154             this.el.child('.roo-tpl-' + this.dataName) :
27155             this.el; 
27156         
27157         el.dom.removeChild(this.nodes[index]);
27158         this.updateIndexes(index);
27159     },
27160
27161     /**
27162      * Refresh an individual node.
27163      * @param {Number} index
27164      */
27165     refreshNode : function(index){
27166         this.onUpdate(this.store, this.store.getAt(index));
27167     },
27168
27169     updateIndexes : function(startIndex, endIndex){
27170         var ns = this.nodes;
27171         startIndex = startIndex || 0;
27172         endIndex = endIndex || ns.length - 1;
27173         for(var i = startIndex; i <= endIndex; i++){
27174             ns[i].nodeIndex = i;
27175         }
27176     },
27177
27178     /**
27179      * Changes the data store this view uses and refresh the view.
27180      * @param {Store} store
27181      */
27182     setStore : function(store, initial){
27183         if(!initial && this.store){
27184             this.store.un("datachanged", this.refresh);
27185             this.store.un("add", this.onAdd);
27186             this.store.un("remove", this.onRemove);
27187             this.store.un("update", this.onUpdate);
27188             this.store.un("clear", this.refresh);
27189             this.store.un("beforeload", this.onBeforeLoad);
27190             this.store.un("load", this.onLoad);
27191             this.store.un("loadexception", this.onLoad);
27192         }
27193         if(store){
27194           
27195             store.on("datachanged", this.refresh, this);
27196             store.on("add", this.onAdd, this);
27197             store.on("remove", this.onRemove, this);
27198             store.on("update", this.onUpdate, this);
27199             store.on("clear", this.refresh, this);
27200             store.on("beforeload", this.onBeforeLoad, this);
27201             store.on("load", this.onLoad, this);
27202             store.on("loadexception", this.onLoad, this);
27203         }
27204         
27205         if(store){
27206             this.refresh();
27207         }
27208     },
27209     /**
27210      * onbeforeLoad - masks the loading area.
27211      *
27212      */
27213     onBeforeLoad : function(store,opts)
27214     {
27215          //Roo.log('onBeforeLoad');   
27216         if (!opts.add) {
27217             this.el.update("");
27218         }
27219         this.el.mask(this.mask ? this.mask : "Loading" ); 
27220     },
27221     onLoad : function ()
27222     {
27223         this.el.unmask();
27224     },
27225     
27226
27227     /**
27228      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27229      * @param {HTMLElement} node
27230      * @return {HTMLElement} The template node
27231      */
27232     findItemFromChild : function(node){
27233         var el = this.dataName  ?
27234             this.el.child('.roo-tpl-' + this.dataName,true) :
27235             this.el.dom; 
27236         
27237         if(!node || node.parentNode == el){
27238                     return node;
27239             }
27240             var p = node.parentNode;
27241             while(p && p != el){
27242             if(p.parentNode == el){
27243                 return p;
27244             }
27245             p = p.parentNode;
27246         }
27247             return null;
27248     },
27249
27250     /** @ignore */
27251     onClick : function(e){
27252         var item = this.findItemFromChild(e.getTarget());
27253         if(item){
27254             var index = this.indexOf(item);
27255             if(this.onItemClick(item, index, e) !== false){
27256                 this.fireEvent("click", this, index, item, e);
27257             }
27258         }else{
27259             this.clearSelections();
27260         }
27261     },
27262
27263     /** @ignore */
27264     onContextMenu : function(e){
27265         var item = this.findItemFromChild(e.getTarget());
27266         if(item){
27267             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27268         }
27269     },
27270
27271     /** @ignore */
27272     onDblClick : function(e){
27273         var item = this.findItemFromChild(e.getTarget());
27274         if(item){
27275             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27276         }
27277     },
27278
27279     onItemClick : function(item, index, e)
27280     {
27281         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27282             return false;
27283         }
27284         if (this.toggleSelect) {
27285             var m = this.isSelected(item) ? 'unselect' : 'select';
27286             //Roo.log(m);
27287             var _t = this;
27288             _t[m](item, true, false);
27289             return true;
27290         }
27291         if(this.multiSelect || this.singleSelect){
27292             if(this.multiSelect && e.shiftKey && this.lastSelection){
27293                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27294             }else{
27295                 this.select(item, this.multiSelect && e.ctrlKey);
27296                 this.lastSelection = item;
27297             }
27298             
27299             if(!this.tickable){
27300                 e.preventDefault();
27301             }
27302             
27303         }
27304         return true;
27305     },
27306
27307     /**
27308      * Get the number of selected nodes.
27309      * @return {Number}
27310      */
27311     getSelectionCount : function(){
27312         return this.selections.length;
27313     },
27314
27315     /**
27316      * Get the currently selected nodes.
27317      * @return {Array} An array of HTMLElements
27318      */
27319     getSelectedNodes : function(){
27320         return this.selections;
27321     },
27322
27323     /**
27324      * Get the indexes of the selected nodes.
27325      * @return {Array}
27326      */
27327     getSelectedIndexes : function(){
27328         var indexes = [], s = this.selections;
27329         for(var i = 0, len = s.length; i < len; i++){
27330             indexes.push(s[i].nodeIndex);
27331         }
27332         return indexes;
27333     },
27334
27335     /**
27336      * Clear all selections
27337      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27338      */
27339     clearSelections : function(suppressEvent){
27340         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27341             this.cmp.elements = this.selections;
27342             this.cmp.removeClass(this.selectedClass);
27343             this.selections = [];
27344             if(!suppressEvent){
27345                 this.fireEvent("selectionchange", this, this.selections);
27346             }
27347         }
27348     },
27349
27350     /**
27351      * Returns true if the passed node is selected
27352      * @param {HTMLElement/Number} node The node or node index
27353      * @return {Boolean}
27354      */
27355     isSelected : function(node){
27356         var s = this.selections;
27357         if(s.length < 1){
27358             return false;
27359         }
27360         node = this.getNode(node);
27361         return s.indexOf(node) !== -1;
27362     },
27363
27364     /**
27365      * Selects nodes.
27366      * @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
27367      * @param {Boolean} keepExisting (optional) true to keep existing selections
27368      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27369      */
27370     select : function(nodeInfo, keepExisting, suppressEvent){
27371         if(nodeInfo instanceof Array){
27372             if(!keepExisting){
27373                 this.clearSelections(true);
27374             }
27375             for(var i = 0, len = nodeInfo.length; i < len; i++){
27376                 this.select(nodeInfo[i], true, true);
27377             }
27378             return;
27379         } 
27380         var node = this.getNode(nodeInfo);
27381         if(!node || this.isSelected(node)){
27382             return; // already selected.
27383         }
27384         if(!keepExisting){
27385             this.clearSelections(true);
27386         }
27387         
27388         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27389             Roo.fly(node).addClass(this.selectedClass);
27390             this.selections.push(node);
27391             if(!suppressEvent){
27392                 this.fireEvent("selectionchange", this, this.selections);
27393             }
27394         }
27395         
27396         
27397     },
27398       /**
27399      * Unselects nodes.
27400      * @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
27401      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27402      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27403      */
27404     unselect : function(nodeInfo, keepExisting, suppressEvent)
27405     {
27406         if(nodeInfo instanceof Array){
27407             Roo.each(this.selections, function(s) {
27408                 this.unselect(s, nodeInfo);
27409             }, this);
27410             return;
27411         }
27412         var node = this.getNode(nodeInfo);
27413         if(!node || !this.isSelected(node)){
27414             //Roo.log("not selected");
27415             return; // not selected.
27416         }
27417         // fireevent???
27418         var ns = [];
27419         Roo.each(this.selections, function(s) {
27420             if (s == node ) {
27421                 Roo.fly(node).removeClass(this.selectedClass);
27422
27423                 return;
27424             }
27425             ns.push(s);
27426         },this);
27427         
27428         this.selections= ns;
27429         this.fireEvent("selectionchange", this, this.selections);
27430     },
27431
27432     /**
27433      * Gets a template node.
27434      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27435      * @return {HTMLElement} The node or null if it wasn't found
27436      */
27437     getNode : function(nodeInfo){
27438         if(typeof nodeInfo == "string"){
27439             return document.getElementById(nodeInfo);
27440         }else if(typeof nodeInfo == "number"){
27441             return this.nodes[nodeInfo];
27442         }
27443         return nodeInfo;
27444     },
27445
27446     /**
27447      * Gets a range template nodes.
27448      * @param {Number} startIndex
27449      * @param {Number} endIndex
27450      * @return {Array} An array of nodes
27451      */
27452     getNodes : function(start, end){
27453         var ns = this.nodes;
27454         start = start || 0;
27455         end = typeof end == "undefined" ? ns.length - 1 : end;
27456         var nodes = [];
27457         if(start <= end){
27458             for(var i = start; i <= end; i++){
27459                 nodes.push(ns[i]);
27460             }
27461         } else{
27462             for(var i = start; i >= end; i--){
27463                 nodes.push(ns[i]);
27464             }
27465         }
27466         return nodes;
27467     },
27468
27469     /**
27470      * Finds the index of the passed node
27471      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27472      * @return {Number} The index of the node or -1
27473      */
27474     indexOf : function(node){
27475         node = this.getNode(node);
27476         if(typeof node.nodeIndex == "number"){
27477             return node.nodeIndex;
27478         }
27479         var ns = this.nodes;
27480         for(var i = 0, len = ns.length; i < len; i++){
27481             if(ns[i] == node){
27482                 return i;
27483             }
27484         }
27485         return -1;
27486     }
27487 });
27488 /*
27489  * Based on:
27490  * Ext JS Library 1.1.1
27491  * Copyright(c) 2006-2007, Ext JS, LLC.
27492  *
27493  * Originally Released Under LGPL - original licence link has changed is not relivant.
27494  *
27495  * Fork - LGPL
27496  * <script type="text/javascript">
27497  */
27498
27499 /**
27500  * @class Roo.JsonView
27501  * @extends Roo.View
27502  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27503 <pre><code>
27504 var view = new Roo.JsonView({
27505     container: "my-element",
27506     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27507     multiSelect: true, 
27508     jsonRoot: "data" 
27509 });
27510
27511 // listen for node click?
27512 view.on("click", function(vw, index, node, e){
27513     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27514 });
27515
27516 // direct load of JSON data
27517 view.load("foobar.php");
27518
27519 // Example from my blog list
27520 var tpl = new Roo.Template(
27521     '&lt;div class="entry"&gt;' +
27522     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27523     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27524     "&lt;/div&gt;&lt;hr /&gt;"
27525 );
27526
27527 var moreView = new Roo.JsonView({
27528     container :  "entry-list", 
27529     template : tpl,
27530     jsonRoot: "posts"
27531 });
27532 moreView.on("beforerender", this.sortEntries, this);
27533 moreView.load({
27534     url: "/blog/get-posts.php",
27535     params: "allposts=true",
27536     text: "Loading Blog Entries..."
27537 });
27538 </code></pre>
27539
27540 * Note: old code is supported with arguments : (container, template, config)
27541
27542
27543  * @constructor
27544  * Create a new JsonView
27545  * 
27546  * @param {Object} config The config object
27547  * 
27548  */
27549 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27550     
27551     
27552     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27553
27554     var um = this.el.getUpdateManager();
27555     um.setRenderer(this);
27556     um.on("update", this.onLoad, this);
27557     um.on("failure", this.onLoadException, this);
27558
27559     /**
27560      * @event beforerender
27561      * Fires before rendering of the downloaded JSON data.
27562      * @param {Roo.JsonView} this
27563      * @param {Object} data The JSON data loaded
27564      */
27565     /**
27566      * @event load
27567      * Fires when data is loaded.
27568      * @param {Roo.JsonView} this
27569      * @param {Object} data The JSON data loaded
27570      * @param {Object} response The raw Connect response object
27571      */
27572     /**
27573      * @event loadexception
27574      * Fires when loading fails.
27575      * @param {Roo.JsonView} this
27576      * @param {Object} response The raw Connect response object
27577      */
27578     this.addEvents({
27579         'beforerender' : true,
27580         'load' : true,
27581         'loadexception' : true
27582     });
27583 };
27584 Roo.extend(Roo.JsonView, Roo.View, {
27585     /**
27586      * @type {String} The root property in the loaded JSON object that contains the data
27587      */
27588     jsonRoot : "",
27589
27590     /**
27591      * Refreshes the view.
27592      */
27593     refresh : function(){
27594         this.clearSelections();
27595         this.el.update("");
27596         var html = [];
27597         var o = this.jsonData;
27598         if(o && o.length > 0){
27599             for(var i = 0, len = o.length; i < len; i++){
27600                 var data = this.prepareData(o[i], i, o);
27601                 html[html.length] = this.tpl.apply(data);
27602             }
27603         }else{
27604             html.push(this.emptyText);
27605         }
27606         this.el.update(html.join(""));
27607         this.nodes = this.el.dom.childNodes;
27608         this.updateIndexes(0);
27609     },
27610
27611     /**
27612      * 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.
27613      * @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:
27614      <pre><code>
27615      view.load({
27616          url: "your-url.php",
27617          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27618          callback: yourFunction,
27619          scope: yourObject, //(optional scope)
27620          discardUrl: false,
27621          nocache: false,
27622          text: "Loading...",
27623          timeout: 30,
27624          scripts: false
27625      });
27626      </code></pre>
27627      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27628      * 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.
27629      * @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}
27630      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27631      * @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.
27632      */
27633     load : function(){
27634         var um = this.el.getUpdateManager();
27635         um.update.apply(um, arguments);
27636     },
27637
27638     // note - render is a standard framework call...
27639     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27640     render : function(el, response){
27641         
27642         this.clearSelections();
27643         this.el.update("");
27644         var o;
27645         try{
27646             if (response != '') {
27647                 o = Roo.util.JSON.decode(response.responseText);
27648                 if(this.jsonRoot){
27649                     
27650                     o = o[this.jsonRoot];
27651                 }
27652             }
27653         } catch(e){
27654         }
27655         /**
27656          * The current JSON data or null
27657          */
27658         this.jsonData = o;
27659         this.beforeRender();
27660         this.refresh();
27661     },
27662
27663 /**
27664  * Get the number of records in the current JSON dataset
27665  * @return {Number}
27666  */
27667     getCount : function(){
27668         return this.jsonData ? this.jsonData.length : 0;
27669     },
27670
27671 /**
27672  * Returns the JSON object for the specified node(s)
27673  * @param {HTMLElement/Array} node The node or an array of nodes
27674  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27675  * you get the JSON object for the node
27676  */
27677     getNodeData : function(node){
27678         if(node instanceof Array){
27679             var data = [];
27680             for(var i = 0, len = node.length; i < len; i++){
27681                 data.push(this.getNodeData(node[i]));
27682             }
27683             return data;
27684         }
27685         return this.jsonData[this.indexOf(node)] || null;
27686     },
27687
27688     beforeRender : function(){
27689         this.snapshot = this.jsonData;
27690         if(this.sortInfo){
27691             this.sort.apply(this, this.sortInfo);
27692         }
27693         this.fireEvent("beforerender", this, this.jsonData);
27694     },
27695
27696     onLoad : function(el, o){
27697         this.fireEvent("load", this, this.jsonData, o);
27698     },
27699
27700     onLoadException : function(el, o){
27701         this.fireEvent("loadexception", this, o);
27702     },
27703
27704 /**
27705  * Filter the data by a specific property.
27706  * @param {String} property A property on your JSON objects
27707  * @param {String/RegExp} value Either string that the property values
27708  * should start with, or a RegExp to test against the property
27709  */
27710     filter : function(property, value){
27711         if(this.jsonData){
27712             var data = [];
27713             var ss = this.snapshot;
27714             if(typeof value == "string"){
27715                 var vlen = value.length;
27716                 if(vlen == 0){
27717                     this.clearFilter();
27718                     return;
27719                 }
27720                 value = value.toLowerCase();
27721                 for(var i = 0, len = ss.length; i < len; i++){
27722                     var o = ss[i];
27723                     if(o[property].substr(0, vlen).toLowerCase() == value){
27724                         data.push(o);
27725                     }
27726                 }
27727             } else if(value.exec){ // regex?
27728                 for(var i = 0, len = ss.length; i < len; i++){
27729                     var o = ss[i];
27730                     if(value.test(o[property])){
27731                         data.push(o);
27732                     }
27733                 }
27734             } else{
27735                 return;
27736             }
27737             this.jsonData = data;
27738             this.refresh();
27739         }
27740     },
27741
27742 /**
27743  * Filter by a function. The passed function will be called with each
27744  * object in the current dataset. If the function returns true the value is kept,
27745  * otherwise it is filtered.
27746  * @param {Function} fn
27747  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27748  */
27749     filterBy : function(fn, scope){
27750         if(this.jsonData){
27751             var data = [];
27752             var ss = this.snapshot;
27753             for(var i = 0, len = ss.length; i < len; i++){
27754                 var o = ss[i];
27755                 if(fn.call(scope || this, o)){
27756                     data.push(o);
27757                 }
27758             }
27759             this.jsonData = data;
27760             this.refresh();
27761         }
27762     },
27763
27764 /**
27765  * Clears the current filter.
27766  */
27767     clearFilter : function(){
27768         if(this.snapshot && this.jsonData != this.snapshot){
27769             this.jsonData = this.snapshot;
27770             this.refresh();
27771         }
27772     },
27773
27774
27775 /**
27776  * Sorts the data for this view and refreshes it.
27777  * @param {String} property A property on your JSON objects to sort on
27778  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27779  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27780  */
27781     sort : function(property, dir, sortType){
27782         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27783         if(this.jsonData){
27784             var p = property;
27785             var dsc = dir && dir.toLowerCase() == "desc";
27786             var f = function(o1, o2){
27787                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27788                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27789                 ;
27790                 if(v1 < v2){
27791                     return dsc ? +1 : -1;
27792                 } else if(v1 > v2){
27793                     return dsc ? -1 : +1;
27794                 } else{
27795                     return 0;
27796                 }
27797             };
27798             this.jsonData.sort(f);
27799             this.refresh();
27800             if(this.jsonData != this.snapshot){
27801                 this.snapshot.sort(f);
27802             }
27803         }
27804     }
27805 });/*
27806  * Based on:
27807  * Ext JS Library 1.1.1
27808  * Copyright(c) 2006-2007, Ext JS, LLC.
27809  *
27810  * Originally Released Under LGPL - original licence link has changed is not relivant.
27811  *
27812  * Fork - LGPL
27813  * <script type="text/javascript">
27814  */
27815  
27816
27817 /**
27818  * @class Roo.ColorPalette
27819  * @extends Roo.Component
27820  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27821  * Here's an example of typical usage:
27822  * <pre><code>
27823 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27824 cp.render('my-div');
27825
27826 cp.on('select', function(palette, selColor){
27827     // do something with selColor
27828 });
27829 </code></pre>
27830  * @constructor
27831  * Create a new ColorPalette
27832  * @param {Object} config The config object
27833  */
27834 Roo.ColorPalette = function(config){
27835     Roo.ColorPalette.superclass.constructor.call(this, config);
27836     this.addEvents({
27837         /**
27838              * @event select
27839              * Fires when a color is selected
27840              * @param {ColorPalette} this
27841              * @param {String} color The 6-digit color hex code (without the # symbol)
27842              */
27843         select: true
27844     });
27845
27846     if(this.handler){
27847         this.on("select", this.handler, this.scope, true);
27848     }
27849 };
27850 Roo.extend(Roo.ColorPalette, Roo.Component, {
27851     /**
27852      * @cfg {String} itemCls
27853      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27854      */
27855     itemCls : "x-color-palette",
27856     /**
27857      * @cfg {String} value
27858      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27859      * the hex codes are case-sensitive.
27860      */
27861     value : null,
27862     clickEvent:'click',
27863     // private
27864     ctype: "Roo.ColorPalette",
27865
27866     /**
27867      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27868      */
27869     allowReselect : false,
27870
27871     /**
27872      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27873      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27874      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27875      * of colors with the width setting until the box is symmetrical.</p>
27876      * <p>You can override individual colors if needed:</p>
27877      * <pre><code>
27878 var cp = new Roo.ColorPalette();
27879 cp.colors[0] = "FF0000";  // change the first box to red
27880 </code></pre>
27881
27882 Or you can provide a custom array of your own for complete control:
27883 <pre><code>
27884 var cp = new Roo.ColorPalette();
27885 cp.colors = ["000000", "993300", "333300"];
27886 </code></pre>
27887      * @type Array
27888      */
27889     colors : [
27890         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27891         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27892         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27893         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27894         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27895     ],
27896
27897     // private
27898     onRender : function(container, position){
27899         var t = new Roo.MasterTemplate(
27900             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27901         );
27902         var c = this.colors;
27903         for(var i = 0, len = c.length; i < len; i++){
27904             t.add([c[i]]);
27905         }
27906         var el = document.createElement("div");
27907         el.className = this.itemCls;
27908         t.overwrite(el);
27909         container.dom.insertBefore(el, position);
27910         this.el = Roo.get(el);
27911         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27912         if(this.clickEvent != 'click'){
27913             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27914         }
27915     },
27916
27917     // private
27918     afterRender : function(){
27919         Roo.ColorPalette.superclass.afterRender.call(this);
27920         if(this.value){
27921             var s = this.value;
27922             this.value = null;
27923             this.select(s);
27924         }
27925     },
27926
27927     // private
27928     handleClick : function(e, t){
27929         e.preventDefault();
27930         if(!this.disabled){
27931             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27932             this.select(c.toUpperCase());
27933         }
27934     },
27935
27936     /**
27937      * Selects the specified color in the palette (fires the select event)
27938      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27939      */
27940     select : function(color){
27941         color = color.replace("#", "");
27942         if(color != this.value || this.allowReselect){
27943             var el = this.el;
27944             if(this.value){
27945                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27946             }
27947             el.child("a.color-"+color).addClass("x-color-palette-sel");
27948             this.value = color;
27949             this.fireEvent("select", this, color);
27950         }
27951     }
27952 });/*
27953  * Based on:
27954  * Ext JS Library 1.1.1
27955  * Copyright(c) 2006-2007, Ext JS, LLC.
27956  *
27957  * Originally Released Under LGPL - original licence link has changed is not relivant.
27958  *
27959  * Fork - LGPL
27960  * <script type="text/javascript">
27961  */
27962  
27963 /**
27964  * @class Roo.DatePicker
27965  * @extends Roo.Component
27966  * Simple date picker class.
27967  * @constructor
27968  * Create a new DatePicker
27969  * @param {Object} config The config object
27970  */
27971 Roo.DatePicker = function(config){
27972     Roo.DatePicker.superclass.constructor.call(this, config);
27973
27974     this.value = config && config.value ?
27975                  config.value.clearTime() : new Date().clearTime();
27976
27977     this.addEvents({
27978         /**
27979              * @event select
27980              * Fires when a date is selected
27981              * @param {DatePicker} this
27982              * @param {Date} date The selected date
27983              */
27984         'select': true,
27985         /**
27986              * @event monthchange
27987              * Fires when the displayed month changes 
27988              * @param {DatePicker} this
27989              * @param {Date} date The selected month
27990              */
27991         'monthchange': true
27992     });
27993
27994     if(this.handler){
27995         this.on("select", this.handler,  this.scope || this);
27996     }
27997     // build the disabledDatesRE
27998     if(!this.disabledDatesRE && this.disabledDates){
27999         var dd = this.disabledDates;
28000         var re = "(?:";
28001         for(var i = 0; i < dd.length; i++){
28002             re += dd[i];
28003             if(i != dd.length-1) {
28004                 re += "|";
28005             }
28006         }
28007         this.disabledDatesRE = new RegExp(re + ")");
28008     }
28009 };
28010
28011 Roo.extend(Roo.DatePicker, Roo.Component, {
28012     /**
28013      * @cfg {String} todayText
28014      * The text to display on the button that selects the current date (defaults to "Today")
28015      */
28016     todayText : "Today",
28017     /**
28018      * @cfg {String} okText
28019      * The text to display on the ok button
28020      */
28021     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28022     /**
28023      * @cfg {String} cancelText
28024      * The text to display on the cancel button
28025      */
28026     cancelText : "Cancel",
28027     /**
28028      * @cfg {String} todayTip
28029      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28030      */
28031     todayTip : "{0} (Spacebar)",
28032     /**
28033      * @cfg {Date} minDate
28034      * Minimum allowable date (JavaScript date object, defaults to null)
28035      */
28036     minDate : null,
28037     /**
28038      * @cfg {Date} maxDate
28039      * Maximum allowable date (JavaScript date object, defaults to null)
28040      */
28041     maxDate : null,
28042     /**
28043      * @cfg {String} minText
28044      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28045      */
28046     minText : "This date is before the minimum date",
28047     /**
28048      * @cfg {String} maxText
28049      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28050      */
28051     maxText : "This date is after the maximum date",
28052     /**
28053      * @cfg {String} format
28054      * The default date format string which can be overriden for localization support.  The format must be
28055      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28056      */
28057     format : "m/d/y",
28058     /**
28059      * @cfg {Array} disabledDays
28060      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28061      */
28062     disabledDays : null,
28063     /**
28064      * @cfg {String} disabledDaysText
28065      * The tooltip to display when the date falls on a disabled day (defaults to "")
28066      */
28067     disabledDaysText : "",
28068     /**
28069      * @cfg {RegExp} disabledDatesRE
28070      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28071      */
28072     disabledDatesRE : null,
28073     /**
28074      * @cfg {String} disabledDatesText
28075      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28076      */
28077     disabledDatesText : "",
28078     /**
28079      * @cfg {Boolean} constrainToViewport
28080      * True to constrain the date picker to the viewport (defaults to true)
28081      */
28082     constrainToViewport : true,
28083     /**
28084      * @cfg {Array} monthNames
28085      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28086      */
28087     monthNames : Date.monthNames,
28088     /**
28089      * @cfg {Array} dayNames
28090      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28091      */
28092     dayNames : Date.dayNames,
28093     /**
28094      * @cfg {String} nextText
28095      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28096      */
28097     nextText: 'Next Month (Control+Right)',
28098     /**
28099      * @cfg {String} prevText
28100      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28101      */
28102     prevText: 'Previous Month (Control+Left)',
28103     /**
28104      * @cfg {String} monthYearText
28105      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28106      */
28107     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28108     /**
28109      * @cfg {Number} startDay
28110      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28111      */
28112     startDay : 0,
28113     /**
28114      * @cfg {Bool} showClear
28115      * Show a clear button (usefull for date form elements that can be blank.)
28116      */
28117     
28118     showClear: false,
28119     
28120     /**
28121      * Sets the value of the date field
28122      * @param {Date} value The date to set
28123      */
28124     setValue : function(value){
28125         var old = this.value;
28126         
28127         if (typeof(value) == 'string') {
28128          
28129             value = Date.parseDate(value, this.format);
28130         }
28131         if (!value) {
28132             value = new Date();
28133         }
28134         
28135         this.value = value.clearTime(true);
28136         if(this.el){
28137             this.update(this.value);
28138         }
28139     },
28140
28141     /**
28142      * Gets the current selected value of the date field
28143      * @return {Date} The selected date
28144      */
28145     getValue : function(){
28146         return this.value;
28147     },
28148
28149     // private
28150     focus : function(){
28151         if(this.el){
28152             this.update(this.activeDate);
28153         }
28154     },
28155
28156     // privateval
28157     onRender : function(container, position){
28158         
28159         var m = [
28160              '<table cellspacing="0">',
28161                 '<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>',
28162                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28163         var dn = this.dayNames;
28164         for(var i = 0; i < 7; i++){
28165             var d = this.startDay+i;
28166             if(d > 6){
28167                 d = d-7;
28168             }
28169             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28170         }
28171         m[m.length] = "</tr></thead><tbody><tr>";
28172         for(var i = 0; i < 42; i++) {
28173             if(i % 7 == 0 && i != 0){
28174                 m[m.length] = "</tr><tr>";
28175             }
28176             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28177         }
28178         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28179             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28180
28181         var el = document.createElement("div");
28182         el.className = "x-date-picker";
28183         el.innerHTML = m.join("");
28184
28185         container.dom.insertBefore(el, position);
28186
28187         this.el = Roo.get(el);
28188         this.eventEl = Roo.get(el.firstChild);
28189
28190         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28191             handler: this.showPrevMonth,
28192             scope: this,
28193             preventDefault:true,
28194             stopDefault:true
28195         });
28196
28197         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28198             handler: this.showNextMonth,
28199             scope: this,
28200             preventDefault:true,
28201             stopDefault:true
28202         });
28203
28204         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28205
28206         this.monthPicker = this.el.down('div.x-date-mp');
28207         this.monthPicker.enableDisplayMode('block');
28208         
28209         var kn = new Roo.KeyNav(this.eventEl, {
28210             "left" : function(e){
28211                 e.ctrlKey ?
28212                     this.showPrevMonth() :
28213                     this.update(this.activeDate.add("d", -1));
28214             },
28215
28216             "right" : function(e){
28217                 e.ctrlKey ?
28218                     this.showNextMonth() :
28219                     this.update(this.activeDate.add("d", 1));
28220             },
28221
28222             "up" : function(e){
28223                 e.ctrlKey ?
28224                     this.showNextYear() :
28225                     this.update(this.activeDate.add("d", -7));
28226             },
28227
28228             "down" : function(e){
28229                 e.ctrlKey ?
28230                     this.showPrevYear() :
28231                     this.update(this.activeDate.add("d", 7));
28232             },
28233
28234             "pageUp" : function(e){
28235                 this.showNextMonth();
28236             },
28237
28238             "pageDown" : function(e){
28239                 this.showPrevMonth();
28240             },
28241
28242             "enter" : function(e){
28243                 e.stopPropagation();
28244                 return true;
28245             },
28246
28247             scope : this
28248         });
28249
28250         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28251
28252         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28253
28254         this.el.unselectable();
28255         
28256         this.cells = this.el.select("table.x-date-inner tbody td");
28257         this.textNodes = this.el.query("table.x-date-inner tbody span");
28258
28259         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28260             text: "&#160;",
28261             tooltip: this.monthYearText
28262         });
28263
28264         this.mbtn.on('click', this.showMonthPicker, this);
28265         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28266
28267
28268         var today = (new Date()).dateFormat(this.format);
28269         
28270         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28271         if (this.showClear) {
28272             baseTb.add( new Roo.Toolbar.Fill());
28273         }
28274         baseTb.add({
28275             text: String.format(this.todayText, today),
28276             tooltip: String.format(this.todayTip, today),
28277             handler: this.selectToday,
28278             scope: this
28279         });
28280         
28281         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28282             
28283         //});
28284         if (this.showClear) {
28285             
28286             baseTb.add( new Roo.Toolbar.Fill());
28287             baseTb.add({
28288                 text: '&#160;',
28289                 cls: 'x-btn-icon x-btn-clear',
28290                 handler: function() {
28291                     //this.value = '';
28292                     this.fireEvent("select", this, '');
28293                 },
28294                 scope: this
28295             });
28296         }
28297         
28298         
28299         if(Roo.isIE){
28300             this.el.repaint();
28301         }
28302         this.update(this.value);
28303     },
28304
28305     createMonthPicker : function(){
28306         if(!this.monthPicker.dom.firstChild){
28307             var buf = ['<table border="0" cellspacing="0">'];
28308             for(var i = 0; i < 6; i++){
28309                 buf.push(
28310                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28311                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28312                     i == 0 ?
28313                     '<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>' :
28314                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28315                 );
28316             }
28317             buf.push(
28318                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28319                     this.okText,
28320                     '</button><button type="button" class="x-date-mp-cancel">',
28321                     this.cancelText,
28322                     '</button></td></tr>',
28323                 '</table>'
28324             );
28325             this.monthPicker.update(buf.join(''));
28326             this.monthPicker.on('click', this.onMonthClick, this);
28327             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28328
28329             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28330             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28331
28332             this.mpMonths.each(function(m, a, i){
28333                 i += 1;
28334                 if((i%2) == 0){
28335                     m.dom.xmonth = 5 + Math.round(i * .5);
28336                 }else{
28337                     m.dom.xmonth = Math.round((i-1) * .5);
28338                 }
28339             });
28340         }
28341     },
28342
28343     showMonthPicker : function(){
28344         this.createMonthPicker();
28345         var size = this.el.getSize();
28346         this.monthPicker.setSize(size);
28347         this.monthPicker.child('table').setSize(size);
28348
28349         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28350         this.updateMPMonth(this.mpSelMonth);
28351         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28352         this.updateMPYear(this.mpSelYear);
28353
28354         this.monthPicker.slideIn('t', {duration:.2});
28355     },
28356
28357     updateMPYear : function(y){
28358         this.mpyear = y;
28359         var ys = this.mpYears.elements;
28360         for(var i = 1; i <= 10; i++){
28361             var td = ys[i-1], y2;
28362             if((i%2) == 0){
28363                 y2 = y + Math.round(i * .5);
28364                 td.firstChild.innerHTML = y2;
28365                 td.xyear = y2;
28366             }else{
28367                 y2 = y - (5-Math.round(i * .5));
28368                 td.firstChild.innerHTML = y2;
28369                 td.xyear = y2;
28370             }
28371             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28372         }
28373     },
28374
28375     updateMPMonth : function(sm){
28376         this.mpMonths.each(function(m, a, i){
28377             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28378         });
28379     },
28380
28381     selectMPMonth: function(m){
28382         
28383     },
28384
28385     onMonthClick : function(e, t){
28386         e.stopEvent();
28387         var el = new Roo.Element(t), pn;
28388         if(el.is('button.x-date-mp-cancel')){
28389             this.hideMonthPicker();
28390         }
28391         else if(el.is('button.x-date-mp-ok')){
28392             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28393             this.hideMonthPicker();
28394         }
28395         else if(pn = el.up('td.x-date-mp-month', 2)){
28396             this.mpMonths.removeClass('x-date-mp-sel');
28397             pn.addClass('x-date-mp-sel');
28398             this.mpSelMonth = pn.dom.xmonth;
28399         }
28400         else if(pn = el.up('td.x-date-mp-year', 2)){
28401             this.mpYears.removeClass('x-date-mp-sel');
28402             pn.addClass('x-date-mp-sel');
28403             this.mpSelYear = pn.dom.xyear;
28404         }
28405         else if(el.is('a.x-date-mp-prev')){
28406             this.updateMPYear(this.mpyear-10);
28407         }
28408         else if(el.is('a.x-date-mp-next')){
28409             this.updateMPYear(this.mpyear+10);
28410         }
28411     },
28412
28413     onMonthDblClick : function(e, t){
28414         e.stopEvent();
28415         var el = new Roo.Element(t), pn;
28416         if(pn = el.up('td.x-date-mp-month', 2)){
28417             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28418             this.hideMonthPicker();
28419         }
28420         else if(pn = el.up('td.x-date-mp-year', 2)){
28421             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28422             this.hideMonthPicker();
28423         }
28424     },
28425
28426     hideMonthPicker : function(disableAnim){
28427         if(this.monthPicker){
28428             if(disableAnim === true){
28429                 this.monthPicker.hide();
28430             }else{
28431                 this.monthPicker.slideOut('t', {duration:.2});
28432             }
28433         }
28434     },
28435
28436     // private
28437     showPrevMonth : function(e){
28438         this.update(this.activeDate.add("mo", -1));
28439     },
28440
28441     // private
28442     showNextMonth : function(e){
28443         this.update(this.activeDate.add("mo", 1));
28444     },
28445
28446     // private
28447     showPrevYear : function(){
28448         this.update(this.activeDate.add("y", -1));
28449     },
28450
28451     // private
28452     showNextYear : function(){
28453         this.update(this.activeDate.add("y", 1));
28454     },
28455
28456     // private
28457     handleMouseWheel : function(e){
28458         var delta = e.getWheelDelta();
28459         if(delta > 0){
28460             this.showPrevMonth();
28461             e.stopEvent();
28462         } else if(delta < 0){
28463             this.showNextMonth();
28464             e.stopEvent();
28465         }
28466     },
28467
28468     // private
28469     handleDateClick : function(e, t){
28470         e.stopEvent();
28471         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28472             this.setValue(new Date(t.dateValue));
28473             this.fireEvent("select", this, this.value);
28474         }
28475     },
28476
28477     // private
28478     selectToday : function(){
28479         this.setValue(new Date().clearTime());
28480         this.fireEvent("select", this, this.value);
28481     },
28482
28483     // private
28484     update : function(date)
28485     {
28486         var vd = this.activeDate;
28487         this.activeDate = date;
28488         if(vd && this.el){
28489             var t = date.getTime();
28490             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28491                 this.cells.removeClass("x-date-selected");
28492                 this.cells.each(function(c){
28493                    if(c.dom.firstChild.dateValue == t){
28494                        c.addClass("x-date-selected");
28495                        setTimeout(function(){
28496                             try{c.dom.firstChild.focus();}catch(e){}
28497                        }, 50);
28498                        return false;
28499                    }
28500                 });
28501                 return;
28502             }
28503         }
28504         
28505         var days = date.getDaysInMonth();
28506         var firstOfMonth = date.getFirstDateOfMonth();
28507         var startingPos = firstOfMonth.getDay()-this.startDay;
28508
28509         if(startingPos <= this.startDay){
28510             startingPos += 7;
28511         }
28512
28513         var pm = date.add("mo", -1);
28514         var prevStart = pm.getDaysInMonth()-startingPos;
28515
28516         var cells = this.cells.elements;
28517         var textEls = this.textNodes;
28518         days += startingPos;
28519
28520         // convert everything to numbers so it's fast
28521         var day = 86400000;
28522         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28523         var today = new Date().clearTime().getTime();
28524         var sel = date.clearTime().getTime();
28525         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28526         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28527         var ddMatch = this.disabledDatesRE;
28528         var ddText = this.disabledDatesText;
28529         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28530         var ddaysText = this.disabledDaysText;
28531         var format = this.format;
28532
28533         var setCellClass = function(cal, cell){
28534             cell.title = "";
28535             var t = d.getTime();
28536             cell.firstChild.dateValue = t;
28537             if(t == today){
28538                 cell.className += " x-date-today";
28539                 cell.title = cal.todayText;
28540             }
28541             if(t == sel){
28542                 cell.className += " x-date-selected";
28543                 setTimeout(function(){
28544                     try{cell.firstChild.focus();}catch(e){}
28545                 }, 50);
28546             }
28547             // disabling
28548             if(t < min) {
28549                 cell.className = " x-date-disabled";
28550                 cell.title = cal.minText;
28551                 return;
28552             }
28553             if(t > max) {
28554                 cell.className = " x-date-disabled";
28555                 cell.title = cal.maxText;
28556                 return;
28557             }
28558             if(ddays){
28559                 if(ddays.indexOf(d.getDay()) != -1){
28560                     cell.title = ddaysText;
28561                     cell.className = " x-date-disabled";
28562                 }
28563             }
28564             if(ddMatch && format){
28565                 var fvalue = d.dateFormat(format);
28566                 if(ddMatch.test(fvalue)){
28567                     cell.title = ddText.replace("%0", fvalue);
28568                     cell.className = " x-date-disabled";
28569                 }
28570             }
28571         };
28572
28573         var i = 0;
28574         for(; i < startingPos; i++) {
28575             textEls[i].innerHTML = (++prevStart);
28576             d.setDate(d.getDate()+1);
28577             cells[i].className = "x-date-prevday";
28578             setCellClass(this, cells[i]);
28579         }
28580         for(; i < days; i++){
28581             intDay = i - startingPos + 1;
28582             textEls[i].innerHTML = (intDay);
28583             d.setDate(d.getDate()+1);
28584             cells[i].className = "x-date-active";
28585             setCellClass(this, cells[i]);
28586         }
28587         var extraDays = 0;
28588         for(; i < 42; i++) {
28589              textEls[i].innerHTML = (++extraDays);
28590              d.setDate(d.getDate()+1);
28591              cells[i].className = "x-date-nextday";
28592              setCellClass(this, cells[i]);
28593         }
28594
28595         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28596         this.fireEvent('monthchange', this, date);
28597         
28598         if(!this.internalRender){
28599             var main = this.el.dom.firstChild;
28600             var w = main.offsetWidth;
28601             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28602             Roo.fly(main).setWidth(w);
28603             this.internalRender = true;
28604             // opera does not respect the auto grow header center column
28605             // then, after it gets a width opera refuses to recalculate
28606             // without a second pass
28607             if(Roo.isOpera && !this.secondPass){
28608                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28609                 this.secondPass = true;
28610                 this.update.defer(10, this, [date]);
28611             }
28612         }
28613         
28614         
28615     }
28616 });        /*
28617  * Based on:
28618  * Ext JS Library 1.1.1
28619  * Copyright(c) 2006-2007, Ext JS, LLC.
28620  *
28621  * Originally Released Under LGPL - original licence link has changed is not relivant.
28622  *
28623  * Fork - LGPL
28624  * <script type="text/javascript">
28625  */
28626 /**
28627  * @class Roo.TabPanel
28628  * @extends Roo.util.Observable
28629  * A lightweight tab container.
28630  * <br><br>
28631  * Usage:
28632  * <pre><code>
28633 // basic tabs 1, built from existing content
28634 var tabs = new Roo.TabPanel("tabs1");
28635 tabs.addTab("script", "View Script");
28636 tabs.addTab("markup", "View Markup");
28637 tabs.activate("script");
28638
28639 // more advanced tabs, built from javascript
28640 var jtabs = new Roo.TabPanel("jtabs");
28641 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28642
28643 // set up the UpdateManager
28644 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28645 var updater = tab2.getUpdateManager();
28646 updater.setDefaultUrl("ajax1.htm");
28647 tab2.on('activate', updater.refresh, updater, true);
28648
28649 // Use setUrl for Ajax loading
28650 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28651 tab3.setUrl("ajax2.htm", null, true);
28652
28653 // Disabled tab
28654 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28655 tab4.disable();
28656
28657 jtabs.activate("jtabs-1");
28658  * </code></pre>
28659  * @constructor
28660  * Create a new TabPanel.
28661  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28662  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28663  */
28664 Roo.TabPanel = function(container, config){
28665     /**
28666     * The container element for this TabPanel.
28667     * @type Roo.Element
28668     */
28669     this.el = Roo.get(container, true);
28670     if(config){
28671         if(typeof config == "boolean"){
28672             this.tabPosition = config ? "bottom" : "top";
28673         }else{
28674             Roo.apply(this, config);
28675         }
28676     }
28677     if(this.tabPosition == "bottom"){
28678         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28679         this.el.addClass("x-tabs-bottom");
28680     }
28681     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28682     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28683     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28684     if(Roo.isIE){
28685         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28686     }
28687     if(this.tabPosition != "bottom"){
28688         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28689          * @type Roo.Element
28690          */
28691         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28692         this.el.addClass("x-tabs-top");
28693     }
28694     this.items = [];
28695
28696     this.bodyEl.setStyle("position", "relative");
28697
28698     this.active = null;
28699     this.activateDelegate = this.activate.createDelegate(this);
28700
28701     this.addEvents({
28702         /**
28703          * @event tabchange
28704          * Fires when the active tab changes
28705          * @param {Roo.TabPanel} this
28706          * @param {Roo.TabPanelItem} activePanel The new active tab
28707          */
28708         "tabchange": true,
28709         /**
28710          * @event beforetabchange
28711          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28712          * @param {Roo.TabPanel} this
28713          * @param {Object} e Set cancel to true on this object to cancel the tab change
28714          * @param {Roo.TabPanelItem} tab The tab being changed to
28715          */
28716         "beforetabchange" : true
28717     });
28718
28719     Roo.EventManager.onWindowResize(this.onResize, this);
28720     this.cpad = this.el.getPadding("lr");
28721     this.hiddenCount = 0;
28722
28723
28724     // toolbar on the tabbar support...
28725     if (this.toolbar) {
28726         var tcfg = this.toolbar;
28727         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28728         this.toolbar = new Roo.Toolbar(tcfg);
28729         if (Roo.isSafari) {
28730             var tbl = tcfg.container.child('table', true);
28731             tbl.setAttribute('width', '100%');
28732         }
28733         
28734     }
28735    
28736
28737
28738     Roo.TabPanel.superclass.constructor.call(this);
28739 };
28740
28741 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28742     /*
28743      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28744      */
28745     tabPosition : "top",
28746     /*
28747      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28748      */
28749     currentTabWidth : 0,
28750     /*
28751      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28752      */
28753     minTabWidth : 40,
28754     /*
28755      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28756      */
28757     maxTabWidth : 250,
28758     /*
28759      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28760      */
28761     preferredTabWidth : 175,
28762     /*
28763      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28764      */
28765     resizeTabs : false,
28766     /*
28767      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28768      */
28769     monitorResize : true,
28770     /*
28771      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28772      */
28773     toolbar : false,
28774
28775     /**
28776      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28777      * @param {String} id The id of the div to use <b>or create</b>
28778      * @param {String} text The text for the tab
28779      * @param {String} content (optional) Content to put in the TabPanelItem body
28780      * @param {Boolean} closable (optional) True to create a close icon on the tab
28781      * @return {Roo.TabPanelItem} The created TabPanelItem
28782      */
28783     addTab : function(id, text, content, closable){
28784         var item = new Roo.TabPanelItem(this, id, text, closable);
28785         this.addTabItem(item);
28786         if(content){
28787             item.setContent(content);
28788         }
28789         return item;
28790     },
28791
28792     /**
28793      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28794      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28795      * @return {Roo.TabPanelItem}
28796      */
28797     getTab : function(id){
28798         return this.items[id];
28799     },
28800
28801     /**
28802      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28803      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28804      */
28805     hideTab : function(id){
28806         var t = this.items[id];
28807         if(!t.isHidden()){
28808            t.setHidden(true);
28809            this.hiddenCount++;
28810            this.autoSizeTabs();
28811         }
28812     },
28813
28814     /**
28815      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28816      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28817      */
28818     unhideTab : function(id){
28819         var t = this.items[id];
28820         if(t.isHidden()){
28821            t.setHidden(false);
28822            this.hiddenCount--;
28823            this.autoSizeTabs();
28824         }
28825     },
28826
28827     /**
28828      * Adds an existing {@link Roo.TabPanelItem}.
28829      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28830      */
28831     addTabItem : function(item){
28832         this.items[item.id] = item;
28833         this.items.push(item);
28834         if(this.resizeTabs){
28835            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28836            this.autoSizeTabs();
28837         }else{
28838             item.autoSize();
28839         }
28840     },
28841
28842     /**
28843      * Removes a {@link Roo.TabPanelItem}.
28844      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28845      */
28846     removeTab : function(id){
28847         var items = this.items;
28848         var tab = items[id];
28849         if(!tab) { return; }
28850         var index = items.indexOf(tab);
28851         if(this.active == tab && items.length > 1){
28852             var newTab = this.getNextAvailable(index);
28853             if(newTab) {
28854                 newTab.activate();
28855             }
28856         }
28857         this.stripEl.dom.removeChild(tab.pnode.dom);
28858         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28859             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28860         }
28861         items.splice(index, 1);
28862         delete this.items[tab.id];
28863         tab.fireEvent("close", tab);
28864         tab.purgeListeners();
28865         this.autoSizeTabs();
28866     },
28867
28868     getNextAvailable : function(start){
28869         var items = this.items;
28870         var index = start;
28871         // look for a next tab that will slide over to
28872         // replace the one being removed
28873         while(index < items.length){
28874             var item = items[++index];
28875             if(item && !item.isHidden()){
28876                 return item;
28877             }
28878         }
28879         // if one isn't found select the previous tab (on the left)
28880         index = start;
28881         while(index >= 0){
28882             var item = items[--index];
28883             if(item && !item.isHidden()){
28884                 return item;
28885             }
28886         }
28887         return null;
28888     },
28889
28890     /**
28891      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28892      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28893      */
28894     disableTab : function(id){
28895         var tab = this.items[id];
28896         if(tab && this.active != tab){
28897             tab.disable();
28898         }
28899     },
28900
28901     /**
28902      * Enables a {@link Roo.TabPanelItem} that is disabled.
28903      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28904      */
28905     enableTab : function(id){
28906         var tab = this.items[id];
28907         tab.enable();
28908     },
28909
28910     /**
28911      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28912      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28913      * @return {Roo.TabPanelItem} The TabPanelItem.
28914      */
28915     activate : function(id){
28916         var tab = this.items[id];
28917         if(!tab){
28918             return null;
28919         }
28920         if(tab == this.active || tab.disabled){
28921             return tab;
28922         }
28923         var e = {};
28924         this.fireEvent("beforetabchange", this, e, tab);
28925         if(e.cancel !== true && !tab.disabled){
28926             if(this.active){
28927                 this.active.hide();
28928             }
28929             this.active = this.items[id];
28930             this.active.show();
28931             this.fireEvent("tabchange", this, this.active);
28932         }
28933         return tab;
28934     },
28935
28936     /**
28937      * Gets the active {@link Roo.TabPanelItem}.
28938      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28939      */
28940     getActiveTab : function(){
28941         return this.active;
28942     },
28943
28944     /**
28945      * Updates the tab body element to fit the height of the container element
28946      * for overflow scrolling
28947      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28948      */
28949     syncHeight : function(targetHeight){
28950         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28951         var bm = this.bodyEl.getMargins();
28952         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28953         this.bodyEl.setHeight(newHeight);
28954         return newHeight;
28955     },
28956
28957     onResize : function(){
28958         if(this.monitorResize){
28959             this.autoSizeTabs();
28960         }
28961     },
28962
28963     /**
28964      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28965      */
28966     beginUpdate : function(){
28967         this.updating = true;
28968     },
28969
28970     /**
28971      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28972      */
28973     endUpdate : function(){
28974         this.updating = false;
28975         this.autoSizeTabs();
28976     },
28977
28978     /**
28979      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28980      */
28981     autoSizeTabs : function(){
28982         var count = this.items.length;
28983         var vcount = count - this.hiddenCount;
28984         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28985             return;
28986         }
28987         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28988         var availWidth = Math.floor(w / vcount);
28989         var b = this.stripBody;
28990         if(b.getWidth() > w){
28991             var tabs = this.items;
28992             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28993             if(availWidth < this.minTabWidth){
28994                 /*if(!this.sleft){    // incomplete scrolling code
28995                     this.createScrollButtons();
28996                 }
28997                 this.showScroll();
28998                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28999             }
29000         }else{
29001             if(this.currentTabWidth < this.preferredTabWidth){
29002                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29003             }
29004         }
29005     },
29006
29007     /**
29008      * Returns the number of tabs in this TabPanel.
29009      * @return {Number}
29010      */
29011      getCount : function(){
29012          return this.items.length;
29013      },
29014
29015     /**
29016      * Resizes all the tabs to the passed width
29017      * @param {Number} The new width
29018      */
29019     setTabWidth : function(width){
29020         this.currentTabWidth = width;
29021         for(var i = 0, len = this.items.length; i < len; i++) {
29022                 if(!this.items[i].isHidden()) {
29023                 this.items[i].setWidth(width);
29024             }
29025         }
29026     },
29027
29028     /**
29029      * Destroys this TabPanel
29030      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29031      */
29032     destroy : function(removeEl){
29033         Roo.EventManager.removeResizeListener(this.onResize, this);
29034         for(var i = 0, len = this.items.length; i < len; i++){
29035             this.items[i].purgeListeners();
29036         }
29037         if(removeEl === true){
29038             this.el.update("");
29039             this.el.remove();
29040         }
29041     }
29042 });
29043
29044 /**
29045  * @class Roo.TabPanelItem
29046  * @extends Roo.util.Observable
29047  * Represents an individual item (tab plus body) in a TabPanel.
29048  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29049  * @param {String} id The id of this TabPanelItem
29050  * @param {String} text The text for the tab of this TabPanelItem
29051  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29052  */
29053 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29054     /**
29055      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29056      * @type Roo.TabPanel
29057      */
29058     this.tabPanel = tabPanel;
29059     /**
29060      * The id for this TabPanelItem
29061      * @type String
29062      */
29063     this.id = id;
29064     /** @private */
29065     this.disabled = false;
29066     /** @private */
29067     this.text = text;
29068     /** @private */
29069     this.loaded = false;
29070     this.closable = closable;
29071
29072     /**
29073      * The body element for this TabPanelItem.
29074      * @type Roo.Element
29075      */
29076     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29077     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29078     this.bodyEl.setStyle("display", "block");
29079     this.bodyEl.setStyle("zoom", "1");
29080     this.hideAction();
29081
29082     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29083     /** @private */
29084     this.el = Roo.get(els.el, true);
29085     this.inner = Roo.get(els.inner, true);
29086     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29087     this.pnode = Roo.get(els.el.parentNode, true);
29088     this.el.on("mousedown", this.onTabMouseDown, this);
29089     this.el.on("click", this.onTabClick, this);
29090     /** @private */
29091     if(closable){
29092         var c = Roo.get(els.close, true);
29093         c.dom.title = this.closeText;
29094         c.addClassOnOver("close-over");
29095         c.on("click", this.closeClick, this);
29096      }
29097
29098     this.addEvents({
29099          /**
29100          * @event activate
29101          * Fires when this tab becomes the active tab.
29102          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29103          * @param {Roo.TabPanelItem} this
29104          */
29105         "activate": true,
29106         /**
29107          * @event beforeclose
29108          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29109          * @param {Roo.TabPanelItem} this
29110          * @param {Object} e Set cancel to true on this object to cancel the close.
29111          */
29112         "beforeclose": true,
29113         /**
29114          * @event close
29115          * Fires when this tab is closed.
29116          * @param {Roo.TabPanelItem} this
29117          */
29118          "close": true,
29119         /**
29120          * @event deactivate
29121          * Fires when this tab is no longer the active tab.
29122          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29123          * @param {Roo.TabPanelItem} this
29124          */
29125          "deactivate" : true
29126     });
29127     this.hidden = false;
29128
29129     Roo.TabPanelItem.superclass.constructor.call(this);
29130 };
29131
29132 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29133     purgeListeners : function(){
29134        Roo.util.Observable.prototype.purgeListeners.call(this);
29135        this.el.removeAllListeners();
29136     },
29137     /**
29138      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29139      */
29140     show : function(){
29141         this.pnode.addClass("on");
29142         this.showAction();
29143         if(Roo.isOpera){
29144             this.tabPanel.stripWrap.repaint();
29145         }
29146         this.fireEvent("activate", this.tabPanel, this);
29147     },
29148
29149     /**
29150      * Returns true if this tab is the active tab.
29151      * @return {Boolean}
29152      */
29153     isActive : function(){
29154         return this.tabPanel.getActiveTab() == this;
29155     },
29156
29157     /**
29158      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29159      */
29160     hide : function(){
29161         this.pnode.removeClass("on");
29162         this.hideAction();
29163         this.fireEvent("deactivate", this.tabPanel, this);
29164     },
29165
29166     hideAction : function(){
29167         this.bodyEl.hide();
29168         this.bodyEl.setStyle("position", "absolute");
29169         this.bodyEl.setLeft("-20000px");
29170         this.bodyEl.setTop("-20000px");
29171     },
29172
29173     showAction : function(){
29174         this.bodyEl.setStyle("position", "relative");
29175         this.bodyEl.setTop("");
29176         this.bodyEl.setLeft("");
29177         this.bodyEl.show();
29178     },
29179
29180     /**
29181      * Set the tooltip for the tab.
29182      * @param {String} tooltip The tab's tooltip
29183      */
29184     setTooltip : function(text){
29185         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29186             this.textEl.dom.qtip = text;
29187             this.textEl.dom.removeAttribute('title');
29188         }else{
29189             this.textEl.dom.title = text;
29190         }
29191     },
29192
29193     onTabClick : function(e){
29194         e.preventDefault();
29195         this.tabPanel.activate(this.id);
29196     },
29197
29198     onTabMouseDown : function(e){
29199         e.preventDefault();
29200         this.tabPanel.activate(this.id);
29201     },
29202
29203     getWidth : function(){
29204         return this.inner.getWidth();
29205     },
29206
29207     setWidth : function(width){
29208         var iwidth = width - this.pnode.getPadding("lr");
29209         this.inner.setWidth(iwidth);
29210         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29211         this.pnode.setWidth(width);
29212     },
29213
29214     /**
29215      * Show or hide the tab
29216      * @param {Boolean} hidden True to hide or false to show.
29217      */
29218     setHidden : function(hidden){
29219         this.hidden = hidden;
29220         this.pnode.setStyle("display", hidden ? "none" : "");
29221     },
29222
29223     /**
29224      * Returns true if this tab is "hidden"
29225      * @return {Boolean}
29226      */
29227     isHidden : function(){
29228         return this.hidden;
29229     },
29230
29231     /**
29232      * Returns the text for this tab
29233      * @return {String}
29234      */
29235     getText : function(){
29236         return this.text;
29237     },
29238
29239     autoSize : function(){
29240         //this.el.beginMeasure();
29241         this.textEl.setWidth(1);
29242         /*
29243          *  #2804 [new] Tabs in Roojs
29244          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29245          */
29246         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29247         //this.el.endMeasure();
29248     },
29249
29250     /**
29251      * Sets the text for the tab (Note: this also sets the tooltip text)
29252      * @param {String} text The tab's text and tooltip
29253      */
29254     setText : function(text){
29255         this.text = text;
29256         this.textEl.update(text);
29257         this.setTooltip(text);
29258         if(!this.tabPanel.resizeTabs){
29259             this.autoSize();
29260         }
29261     },
29262     /**
29263      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29264      */
29265     activate : function(){
29266         this.tabPanel.activate(this.id);
29267     },
29268
29269     /**
29270      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29271      */
29272     disable : function(){
29273         if(this.tabPanel.active != this){
29274             this.disabled = true;
29275             this.pnode.addClass("disabled");
29276         }
29277     },
29278
29279     /**
29280      * Enables this TabPanelItem if it was previously disabled.
29281      */
29282     enable : function(){
29283         this.disabled = false;
29284         this.pnode.removeClass("disabled");
29285     },
29286
29287     /**
29288      * Sets the content for this TabPanelItem.
29289      * @param {String} content The content
29290      * @param {Boolean} loadScripts true to look for and load scripts
29291      */
29292     setContent : function(content, loadScripts){
29293         this.bodyEl.update(content, loadScripts);
29294     },
29295
29296     /**
29297      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29298      * @return {Roo.UpdateManager} The UpdateManager
29299      */
29300     getUpdateManager : function(){
29301         return this.bodyEl.getUpdateManager();
29302     },
29303
29304     /**
29305      * Set a URL to be used to load the content for this TabPanelItem.
29306      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29307      * @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)
29308      * @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)
29309      * @return {Roo.UpdateManager} The UpdateManager
29310      */
29311     setUrl : function(url, params, loadOnce){
29312         if(this.refreshDelegate){
29313             this.un('activate', this.refreshDelegate);
29314         }
29315         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29316         this.on("activate", this.refreshDelegate);
29317         return this.bodyEl.getUpdateManager();
29318     },
29319
29320     /** @private */
29321     _handleRefresh : function(url, params, loadOnce){
29322         if(!loadOnce || !this.loaded){
29323             var updater = this.bodyEl.getUpdateManager();
29324             updater.update(url, params, this._setLoaded.createDelegate(this));
29325         }
29326     },
29327
29328     /**
29329      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29330      *   Will fail silently if the setUrl method has not been called.
29331      *   This does not activate the panel, just updates its content.
29332      */
29333     refresh : function(){
29334         if(this.refreshDelegate){
29335            this.loaded = false;
29336            this.refreshDelegate();
29337         }
29338     },
29339
29340     /** @private */
29341     _setLoaded : function(){
29342         this.loaded = true;
29343     },
29344
29345     /** @private */
29346     closeClick : function(e){
29347         var o = {};
29348         e.stopEvent();
29349         this.fireEvent("beforeclose", this, o);
29350         if(o.cancel !== true){
29351             this.tabPanel.removeTab(this.id);
29352         }
29353     },
29354     /**
29355      * The text displayed in the tooltip for the close icon.
29356      * @type String
29357      */
29358     closeText : "Close this tab"
29359 });
29360
29361 /** @private */
29362 Roo.TabPanel.prototype.createStrip = function(container){
29363     var strip = document.createElement("div");
29364     strip.className = "x-tabs-wrap";
29365     container.appendChild(strip);
29366     return strip;
29367 };
29368 /** @private */
29369 Roo.TabPanel.prototype.createStripList = function(strip){
29370     // div wrapper for retard IE
29371     // returns the "tr" element.
29372     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29373         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29374         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29375     return strip.firstChild.firstChild.firstChild.firstChild;
29376 };
29377 /** @private */
29378 Roo.TabPanel.prototype.createBody = function(container){
29379     var body = document.createElement("div");
29380     Roo.id(body, "tab-body");
29381     Roo.fly(body).addClass("x-tabs-body");
29382     container.appendChild(body);
29383     return body;
29384 };
29385 /** @private */
29386 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29387     var body = Roo.getDom(id);
29388     if(!body){
29389         body = document.createElement("div");
29390         body.id = id;
29391     }
29392     Roo.fly(body).addClass("x-tabs-item-body");
29393     bodyEl.insertBefore(body, bodyEl.firstChild);
29394     return body;
29395 };
29396 /** @private */
29397 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29398     var td = document.createElement("td");
29399     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29400     //stripEl.appendChild(td);
29401     if(closable){
29402         td.className = "x-tabs-closable";
29403         if(!this.closeTpl){
29404             this.closeTpl = new Roo.Template(
29405                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29406                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29407                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29408             );
29409         }
29410         var el = this.closeTpl.overwrite(td, {"text": text});
29411         var close = el.getElementsByTagName("div")[0];
29412         var inner = el.getElementsByTagName("em")[0];
29413         return {"el": el, "close": close, "inner": inner};
29414     } else {
29415         if(!this.tabTpl){
29416             this.tabTpl = new Roo.Template(
29417                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29418                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29419             );
29420         }
29421         var el = this.tabTpl.overwrite(td, {"text": text});
29422         var inner = el.getElementsByTagName("em")[0];
29423         return {"el": el, "inner": inner};
29424     }
29425 };/*
29426  * Based on:
29427  * Ext JS Library 1.1.1
29428  * Copyright(c) 2006-2007, Ext JS, LLC.
29429  *
29430  * Originally Released Under LGPL - original licence link has changed is not relivant.
29431  *
29432  * Fork - LGPL
29433  * <script type="text/javascript">
29434  */
29435
29436 /**
29437  * @class Roo.Button
29438  * @extends Roo.util.Observable
29439  * Simple Button class
29440  * @cfg {String} text The button text
29441  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29442  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29443  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29444  * @cfg {Object} scope The scope of the handler
29445  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29446  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29447  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29448  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29449  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29450  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29451    applies if enableToggle = true)
29452  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29453  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29454   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29455  * @constructor
29456  * Create a new button
29457  * @param {Object} config The config object
29458  */
29459 Roo.Button = function(renderTo, config)
29460 {
29461     if (!config) {
29462         config = renderTo;
29463         renderTo = config.renderTo || false;
29464     }
29465     
29466     Roo.apply(this, config);
29467     this.addEvents({
29468         /**
29469              * @event click
29470              * Fires when this button is clicked
29471              * @param {Button} this
29472              * @param {EventObject} e The click event
29473              */
29474             "click" : true,
29475         /**
29476              * @event toggle
29477              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29478              * @param {Button} this
29479              * @param {Boolean} pressed
29480              */
29481             "toggle" : true,
29482         /**
29483              * @event mouseover
29484              * Fires when the mouse hovers over the button
29485              * @param {Button} this
29486              * @param {Event} e The event object
29487              */
29488         'mouseover' : true,
29489         /**
29490              * @event mouseout
29491              * Fires when the mouse exits the button
29492              * @param {Button} this
29493              * @param {Event} e The event object
29494              */
29495         'mouseout': true,
29496          /**
29497              * @event render
29498              * Fires when the button is rendered
29499              * @param {Button} this
29500              */
29501         'render': true
29502     });
29503     if(this.menu){
29504         this.menu = Roo.menu.MenuMgr.get(this.menu);
29505     }
29506     // register listeners first!!  - so render can be captured..
29507     Roo.util.Observable.call(this);
29508     if(renderTo){
29509         this.render(renderTo);
29510     }
29511     
29512   
29513 };
29514
29515 Roo.extend(Roo.Button, Roo.util.Observable, {
29516     /**
29517      * 
29518      */
29519     
29520     /**
29521      * Read-only. True if this button is hidden
29522      * @type Boolean
29523      */
29524     hidden : false,
29525     /**
29526      * Read-only. True if this button is disabled
29527      * @type Boolean
29528      */
29529     disabled : false,
29530     /**
29531      * Read-only. True if this button is pressed (only if enableToggle = true)
29532      * @type Boolean
29533      */
29534     pressed : false,
29535
29536     /**
29537      * @cfg {Number} tabIndex 
29538      * The DOM tabIndex for this button (defaults to undefined)
29539      */
29540     tabIndex : undefined,
29541
29542     /**
29543      * @cfg {Boolean} enableToggle
29544      * True to enable pressed/not pressed toggling (defaults to false)
29545      */
29546     enableToggle: false,
29547     /**
29548      * @cfg {Roo.menu.Menu} menu
29549      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29550      */
29551     menu : undefined,
29552     /**
29553      * @cfg {String} menuAlign
29554      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29555      */
29556     menuAlign : "tl-bl?",
29557
29558     /**
29559      * @cfg {String} iconCls
29560      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29561      */
29562     iconCls : undefined,
29563     /**
29564      * @cfg {String} type
29565      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29566      */
29567     type : 'button',
29568
29569     // private
29570     menuClassTarget: 'tr',
29571
29572     /**
29573      * @cfg {String} clickEvent
29574      * The type of event to map to the button's event handler (defaults to 'click')
29575      */
29576     clickEvent : 'click',
29577
29578     /**
29579      * @cfg {Boolean} handleMouseEvents
29580      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29581      */
29582     handleMouseEvents : true,
29583
29584     /**
29585      * @cfg {String} tooltipType
29586      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29587      */
29588     tooltipType : 'qtip',
29589
29590     /**
29591      * @cfg {String} cls
29592      * A CSS class to apply to the button's main element.
29593      */
29594     
29595     /**
29596      * @cfg {Roo.Template} template (Optional)
29597      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29598      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29599      * require code modifications if required elements (e.g. a button) aren't present.
29600      */
29601
29602     // private
29603     render : function(renderTo){
29604         var btn;
29605         if(this.hideParent){
29606             this.parentEl = Roo.get(renderTo);
29607         }
29608         if(!this.dhconfig){
29609             if(!this.template){
29610                 if(!Roo.Button.buttonTemplate){
29611                     // hideous table template
29612                     Roo.Button.buttonTemplate = new Roo.Template(
29613                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29614                         '<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>',
29615                         "</tr></tbody></table>");
29616                 }
29617                 this.template = Roo.Button.buttonTemplate;
29618             }
29619             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29620             var btnEl = btn.child("button:first");
29621             btnEl.on('focus', this.onFocus, this);
29622             btnEl.on('blur', this.onBlur, this);
29623             if(this.cls){
29624                 btn.addClass(this.cls);
29625             }
29626             if(this.icon){
29627                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29628             }
29629             if(this.iconCls){
29630                 btnEl.addClass(this.iconCls);
29631                 if(!this.cls){
29632                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29633                 }
29634             }
29635             if(this.tabIndex !== undefined){
29636                 btnEl.dom.tabIndex = this.tabIndex;
29637             }
29638             if(this.tooltip){
29639                 if(typeof this.tooltip == 'object'){
29640                     Roo.QuickTips.tips(Roo.apply({
29641                           target: btnEl.id
29642                     }, this.tooltip));
29643                 } else {
29644                     btnEl.dom[this.tooltipType] = this.tooltip;
29645                 }
29646             }
29647         }else{
29648             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29649         }
29650         this.el = btn;
29651         if(this.id){
29652             this.el.dom.id = this.el.id = this.id;
29653         }
29654         if(this.menu){
29655             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29656             this.menu.on("show", this.onMenuShow, this);
29657             this.menu.on("hide", this.onMenuHide, this);
29658         }
29659         btn.addClass("x-btn");
29660         if(Roo.isIE && !Roo.isIE7){
29661             this.autoWidth.defer(1, this);
29662         }else{
29663             this.autoWidth();
29664         }
29665         if(this.handleMouseEvents){
29666             btn.on("mouseover", this.onMouseOver, this);
29667             btn.on("mouseout", this.onMouseOut, this);
29668             btn.on("mousedown", this.onMouseDown, this);
29669         }
29670         btn.on(this.clickEvent, this.onClick, this);
29671         //btn.on("mouseup", this.onMouseUp, this);
29672         if(this.hidden){
29673             this.hide();
29674         }
29675         if(this.disabled){
29676             this.disable();
29677         }
29678         Roo.ButtonToggleMgr.register(this);
29679         if(this.pressed){
29680             this.el.addClass("x-btn-pressed");
29681         }
29682         if(this.repeat){
29683             var repeater = new Roo.util.ClickRepeater(btn,
29684                 typeof this.repeat == "object" ? this.repeat : {}
29685             );
29686             repeater.on("click", this.onClick,  this);
29687         }
29688         
29689         this.fireEvent('render', this);
29690         
29691     },
29692     /**
29693      * Returns the button's underlying element
29694      * @return {Roo.Element} The element
29695      */
29696     getEl : function(){
29697         return this.el;  
29698     },
29699     
29700     /**
29701      * Destroys this Button and removes any listeners.
29702      */
29703     destroy : function(){
29704         Roo.ButtonToggleMgr.unregister(this);
29705         this.el.removeAllListeners();
29706         this.purgeListeners();
29707         this.el.remove();
29708     },
29709
29710     // private
29711     autoWidth : function(){
29712         if(this.el){
29713             this.el.setWidth("auto");
29714             if(Roo.isIE7 && Roo.isStrict){
29715                 var ib = this.el.child('button');
29716                 if(ib && ib.getWidth() > 20){
29717                     ib.clip();
29718                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29719                 }
29720             }
29721             if(this.minWidth){
29722                 if(this.hidden){
29723                     this.el.beginMeasure();
29724                 }
29725                 if(this.el.getWidth() < this.minWidth){
29726                     this.el.setWidth(this.minWidth);
29727                 }
29728                 if(this.hidden){
29729                     this.el.endMeasure();
29730                 }
29731             }
29732         }
29733     },
29734
29735     /**
29736      * Assigns this button's click handler
29737      * @param {Function} handler The function to call when the button is clicked
29738      * @param {Object} scope (optional) Scope for the function passed in
29739      */
29740     setHandler : function(handler, scope){
29741         this.handler = handler;
29742         this.scope = scope;  
29743     },
29744     
29745     /**
29746      * Sets this button's text
29747      * @param {String} text The button text
29748      */
29749     setText : function(text){
29750         this.text = text;
29751         if(this.el){
29752             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29753         }
29754         this.autoWidth();
29755     },
29756     
29757     /**
29758      * Gets the text for this button
29759      * @return {String} The button text
29760      */
29761     getText : function(){
29762         return this.text;  
29763     },
29764     
29765     /**
29766      * Show this button
29767      */
29768     show: function(){
29769         this.hidden = false;
29770         if(this.el){
29771             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29772         }
29773     },
29774     
29775     /**
29776      * Hide this button
29777      */
29778     hide: function(){
29779         this.hidden = true;
29780         if(this.el){
29781             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29782         }
29783     },
29784     
29785     /**
29786      * Convenience function for boolean show/hide
29787      * @param {Boolean} visible True to show, false to hide
29788      */
29789     setVisible: function(visible){
29790         if(visible) {
29791             this.show();
29792         }else{
29793             this.hide();
29794         }
29795     },
29796     
29797     /**
29798      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29799      * @param {Boolean} state (optional) Force a particular state
29800      */
29801     toggle : function(state){
29802         state = state === undefined ? !this.pressed : state;
29803         if(state != this.pressed){
29804             if(state){
29805                 this.el.addClass("x-btn-pressed");
29806                 this.pressed = true;
29807                 this.fireEvent("toggle", this, true);
29808             }else{
29809                 this.el.removeClass("x-btn-pressed");
29810                 this.pressed = false;
29811                 this.fireEvent("toggle", this, false);
29812             }
29813             if(this.toggleHandler){
29814                 this.toggleHandler.call(this.scope || this, this, state);
29815             }
29816         }
29817     },
29818     
29819     /**
29820      * Focus the button
29821      */
29822     focus : function(){
29823         this.el.child('button:first').focus();
29824     },
29825     
29826     /**
29827      * Disable this button
29828      */
29829     disable : function(){
29830         if(this.el){
29831             this.el.addClass("x-btn-disabled");
29832         }
29833         this.disabled = true;
29834     },
29835     
29836     /**
29837      * Enable this button
29838      */
29839     enable : function(){
29840         if(this.el){
29841             this.el.removeClass("x-btn-disabled");
29842         }
29843         this.disabled = false;
29844     },
29845
29846     /**
29847      * Convenience function for boolean enable/disable
29848      * @param {Boolean} enabled True to enable, false to disable
29849      */
29850     setDisabled : function(v){
29851         this[v !== true ? "enable" : "disable"]();
29852     },
29853
29854     // private
29855     onClick : function(e)
29856     {
29857         if(e){
29858             e.preventDefault();
29859         }
29860         if(e.button != 0){
29861             return;
29862         }
29863         if(!this.disabled){
29864             if(this.enableToggle){
29865                 this.toggle();
29866             }
29867             if(this.menu && !this.menu.isVisible()){
29868                 this.menu.show(this.el, this.menuAlign);
29869             }
29870             this.fireEvent("click", this, e);
29871             if(this.handler){
29872                 this.el.removeClass("x-btn-over");
29873                 this.handler.call(this.scope || this, this, e);
29874             }
29875         }
29876     },
29877     // private
29878     onMouseOver : function(e){
29879         if(!this.disabled){
29880             this.el.addClass("x-btn-over");
29881             this.fireEvent('mouseover', this, e);
29882         }
29883     },
29884     // private
29885     onMouseOut : function(e){
29886         if(!e.within(this.el,  true)){
29887             this.el.removeClass("x-btn-over");
29888             this.fireEvent('mouseout', this, e);
29889         }
29890     },
29891     // private
29892     onFocus : function(e){
29893         if(!this.disabled){
29894             this.el.addClass("x-btn-focus");
29895         }
29896     },
29897     // private
29898     onBlur : function(e){
29899         this.el.removeClass("x-btn-focus");
29900     },
29901     // private
29902     onMouseDown : function(e){
29903         if(!this.disabled && e.button == 0){
29904             this.el.addClass("x-btn-click");
29905             Roo.get(document).on('mouseup', this.onMouseUp, this);
29906         }
29907     },
29908     // private
29909     onMouseUp : function(e){
29910         if(e.button == 0){
29911             this.el.removeClass("x-btn-click");
29912             Roo.get(document).un('mouseup', this.onMouseUp, this);
29913         }
29914     },
29915     // private
29916     onMenuShow : function(e){
29917         this.el.addClass("x-btn-menu-active");
29918     },
29919     // private
29920     onMenuHide : function(e){
29921         this.el.removeClass("x-btn-menu-active");
29922     }   
29923 });
29924
29925 // Private utility class used by Button
29926 Roo.ButtonToggleMgr = function(){
29927    var groups = {};
29928    
29929    function toggleGroup(btn, state){
29930        if(state){
29931            var g = groups[btn.toggleGroup];
29932            for(var i = 0, l = g.length; i < l; i++){
29933                if(g[i] != btn){
29934                    g[i].toggle(false);
29935                }
29936            }
29937        }
29938    }
29939    
29940    return {
29941        register : function(btn){
29942            if(!btn.toggleGroup){
29943                return;
29944            }
29945            var g = groups[btn.toggleGroup];
29946            if(!g){
29947                g = groups[btn.toggleGroup] = [];
29948            }
29949            g.push(btn);
29950            btn.on("toggle", toggleGroup);
29951        },
29952        
29953        unregister : function(btn){
29954            if(!btn.toggleGroup){
29955                return;
29956            }
29957            var g = groups[btn.toggleGroup];
29958            if(g){
29959                g.remove(btn);
29960                btn.un("toggle", toggleGroup);
29961            }
29962        }
29963    };
29964 }();/*
29965  * Based on:
29966  * Ext JS Library 1.1.1
29967  * Copyright(c) 2006-2007, Ext JS, LLC.
29968  *
29969  * Originally Released Under LGPL - original licence link has changed is not relivant.
29970  *
29971  * Fork - LGPL
29972  * <script type="text/javascript">
29973  */
29974  
29975 /**
29976  * @class Roo.SplitButton
29977  * @extends Roo.Button
29978  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29979  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29980  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29981  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29982  * @cfg {String} arrowTooltip The title attribute of the arrow
29983  * @constructor
29984  * Create a new menu button
29985  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29986  * @param {Object} config The config object
29987  */
29988 Roo.SplitButton = function(renderTo, config){
29989     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29990     /**
29991      * @event arrowclick
29992      * Fires when this button's arrow is clicked
29993      * @param {SplitButton} this
29994      * @param {EventObject} e The click event
29995      */
29996     this.addEvents({"arrowclick":true});
29997 };
29998
29999 Roo.extend(Roo.SplitButton, Roo.Button, {
30000     render : function(renderTo){
30001         // this is one sweet looking template!
30002         var tpl = new Roo.Template(
30003             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30004             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30005             '<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>',
30006             "</tbody></table></td><td>",
30007             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30008             '<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>',
30009             "</tbody></table></td></tr></table>"
30010         );
30011         var btn = tpl.append(renderTo, [this.text, this.type], true);
30012         var btnEl = btn.child("button");
30013         if(this.cls){
30014             btn.addClass(this.cls);
30015         }
30016         if(this.icon){
30017             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30018         }
30019         if(this.iconCls){
30020             btnEl.addClass(this.iconCls);
30021             if(!this.cls){
30022                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30023             }
30024         }
30025         this.el = btn;
30026         if(this.handleMouseEvents){
30027             btn.on("mouseover", this.onMouseOver, this);
30028             btn.on("mouseout", this.onMouseOut, this);
30029             btn.on("mousedown", this.onMouseDown, this);
30030             btn.on("mouseup", this.onMouseUp, this);
30031         }
30032         btn.on(this.clickEvent, this.onClick, this);
30033         if(this.tooltip){
30034             if(typeof this.tooltip == 'object'){
30035                 Roo.QuickTips.tips(Roo.apply({
30036                       target: btnEl.id
30037                 }, this.tooltip));
30038             } else {
30039                 btnEl.dom[this.tooltipType] = this.tooltip;
30040             }
30041         }
30042         if(this.arrowTooltip){
30043             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30044         }
30045         if(this.hidden){
30046             this.hide();
30047         }
30048         if(this.disabled){
30049             this.disable();
30050         }
30051         if(this.pressed){
30052             this.el.addClass("x-btn-pressed");
30053         }
30054         if(Roo.isIE && !Roo.isIE7){
30055             this.autoWidth.defer(1, this);
30056         }else{
30057             this.autoWidth();
30058         }
30059         if(this.menu){
30060             this.menu.on("show", this.onMenuShow, this);
30061             this.menu.on("hide", this.onMenuHide, this);
30062         }
30063         this.fireEvent('render', this);
30064     },
30065
30066     // private
30067     autoWidth : function(){
30068         if(this.el){
30069             var tbl = this.el.child("table:first");
30070             var tbl2 = this.el.child("table:last");
30071             this.el.setWidth("auto");
30072             tbl.setWidth("auto");
30073             if(Roo.isIE7 && Roo.isStrict){
30074                 var ib = this.el.child('button:first');
30075                 if(ib && ib.getWidth() > 20){
30076                     ib.clip();
30077                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30078                 }
30079             }
30080             if(this.minWidth){
30081                 if(this.hidden){
30082                     this.el.beginMeasure();
30083                 }
30084                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30085                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30086                 }
30087                 if(this.hidden){
30088                     this.el.endMeasure();
30089                 }
30090             }
30091             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30092         } 
30093     },
30094     /**
30095      * Sets this button's click handler
30096      * @param {Function} handler The function to call when the button is clicked
30097      * @param {Object} scope (optional) Scope for the function passed above
30098      */
30099     setHandler : function(handler, scope){
30100         this.handler = handler;
30101         this.scope = scope;  
30102     },
30103     
30104     /**
30105      * Sets this button's arrow click handler
30106      * @param {Function} handler The function to call when the arrow is clicked
30107      * @param {Object} scope (optional) Scope for the function passed above
30108      */
30109     setArrowHandler : function(handler, scope){
30110         this.arrowHandler = handler;
30111         this.scope = scope;  
30112     },
30113     
30114     /**
30115      * Focus the button
30116      */
30117     focus : function(){
30118         if(this.el){
30119             this.el.child("button:first").focus();
30120         }
30121     },
30122
30123     // private
30124     onClick : function(e){
30125         e.preventDefault();
30126         if(!this.disabled){
30127             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30128                 if(this.menu && !this.menu.isVisible()){
30129                     this.menu.show(this.el, this.menuAlign);
30130                 }
30131                 this.fireEvent("arrowclick", this, e);
30132                 if(this.arrowHandler){
30133                     this.arrowHandler.call(this.scope || this, this, e);
30134                 }
30135             }else{
30136                 this.fireEvent("click", this, e);
30137                 if(this.handler){
30138                     this.handler.call(this.scope || this, this, e);
30139                 }
30140             }
30141         }
30142     },
30143     // private
30144     onMouseDown : function(e){
30145         if(!this.disabled){
30146             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30147         }
30148     },
30149     // private
30150     onMouseUp : function(e){
30151         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30152     }   
30153 });
30154
30155
30156 // backwards compat
30157 Roo.MenuButton = Roo.SplitButton;/*
30158  * Based on:
30159  * Ext JS Library 1.1.1
30160  * Copyright(c) 2006-2007, Ext JS, LLC.
30161  *
30162  * Originally Released Under LGPL - original licence link has changed is not relivant.
30163  *
30164  * Fork - LGPL
30165  * <script type="text/javascript">
30166  */
30167
30168 /**
30169  * @class Roo.Toolbar
30170  * @children   Roo.Toolbar.Item Roo.form.Field
30171  * Basic Toolbar class.
30172  * @constructor
30173  * Creates a new Toolbar
30174  * @param {Object} container The config object
30175  */ 
30176 Roo.Toolbar = function(container, buttons, config)
30177 {
30178     /// old consturctor format still supported..
30179     if(container instanceof Array){ // omit the container for later rendering
30180         buttons = container;
30181         config = buttons;
30182         container = null;
30183     }
30184     if (typeof(container) == 'object' && container.xtype) {
30185         config = container;
30186         container = config.container;
30187         buttons = config.buttons || []; // not really - use items!!
30188     }
30189     var xitems = [];
30190     if (config && config.items) {
30191         xitems = config.items;
30192         delete config.items;
30193     }
30194     Roo.apply(this, config);
30195     this.buttons = buttons;
30196     
30197     if(container){
30198         this.render(container);
30199     }
30200     this.xitems = xitems;
30201     Roo.each(xitems, function(b) {
30202         this.add(b);
30203     }, this);
30204     
30205 };
30206
30207 Roo.Toolbar.prototype = {
30208     /**
30209      * @cfg {Array} items
30210      * array of button configs or elements to add (will be converted to a MixedCollection)
30211      */
30212     items: false,
30213     /**
30214      * @cfg {String/HTMLElement/Element} container
30215      * The id or element that will contain the toolbar
30216      */
30217     // private
30218     render : function(ct){
30219         this.el = Roo.get(ct);
30220         if(this.cls){
30221             this.el.addClass(this.cls);
30222         }
30223         // using a table allows for vertical alignment
30224         // 100% width is needed by Safari...
30225         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30226         this.tr = this.el.child("tr", true);
30227         var autoId = 0;
30228         this.items = new Roo.util.MixedCollection(false, function(o){
30229             return o.id || ("item" + (++autoId));
30230         });
30231         if(this.buttons){
30232             this.add.apply(this, this.buttons);
30233             delete this.buttons;
30234         }
30235     },
30236
30237     /**
30238      * Adds element(s) to the toolbar -- this function takes a variable number of 
30239      * arguments of mixed type and adds them to the toolbar.
30240      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30241      * <ul>
30242      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30243      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30244      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30245      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30246      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30247      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30248      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30249      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30250      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30251      * </ul>
30252      * @param {Mixed} arg2
30253      * @param {Mixed} etc.
30254      */
30255     add : function(){
30256         var a = arguments, l = a.length;
30257         for(var i = 0; i < l; i++){
30258             this._add(a[i]);
30259         }
30260     },
30261     // private..
30262     _add : function(el) {
30263         
30264         if (el.xtype) {
30265             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30266         }
30267         
30268         if (el.applyTo){ // some kind of form field
30269             return this.addField(el);
30270         } 
30271         if (el.render){ // some kind of Toolbar.Item
30272             return this.addItem(el);
30273         }
30274         if (typeof el == "string"){ // string
30275             if(el == "separator" || el == "-"){
30276                 return this.addSeparator();
30277             }
30278             if (el == " "){
30279                 return this.addSpacer();
30280             }
30281             if(el == "->"){
30282                 return this.addFill();
30283             }
30284             return this.addText(el);
30285             
30286         }
30287         if(el.tagName){ // element
30288             return this.addElement(el);
30289         }
30290         if(typeof el == "object"){ // must be button config?
30291             return this.addButton(el);
30292         }
30293         // and now what?!?!
30294         return false;
30295         
30296     },
30297     
30298     /**
30299      * Add an Xtype element
30300      * @param {Object} xtype Xtype Object
30301      * @return {Object} created Object
30302      */
30303     addxtype : function(e){
30304         return this.add(e);  
30305     },
30306     
30307     /**
30308      * Returns the Element for this toolbar.
30309      * @return {Roo.Element}
30310      */
30311     getEl : function(){
30312         return this.el;  
30313     },
30314     
30315     /**
30316      * Adds a separator
30317      * @return {Roo.Toolbar.Item} The separator item
30318      */
30319     addSeparator : function(){
30320         return this.addItem(new Roo.Toolbar.Separator());
30321     },
30322
30323     /**
30324      * Adds a spacer element
30325      * @return {Roo.Toolbar.Spacer} The spacer item
30326      */
30327     addSpacer : function(){
30328         return this.addItem(new Roo.Toolbar.Spacer());
30329     },
30330
30331     /**
30332      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30333      * @return {Roo.Toolbar.Fill} The fill item
30334      */
30335     addFill : function(){
30336         return this.addItem(new Roo.Toolbar.Fill());
30337     },
30338
30339     /**
30340      * Adds any standard HTML element to the toolbar
30341      * @param {String/HTMLElement/Element} el The element or id of the element to add
30342      * @return {Roo.Toolbar.Item} The element's item
30343      */
30344     addElement : function(el){
30345         return this.addItem(new Roo.Toolbar.Item(el));
30346     },
30347     /**
30348      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30349      * @type Roo.util.MixedCollection  
30350      */
30351     items : false,
30352      
30353     /**
30354      * Adds any Toolbar.Item or subclass
30355      * @param {Roo.Toolbar.Item} item
30356      * @return {Roo.Toolbar.Item} The item
30357      */
30358     addItem : function(item){
30359         var td = this.nextBlock();
30360         item.render(td);
30361         this.items.add(item);
30362         return item;
30363     },
30364     
30365     /**
30366      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30367      * @param {Object/Array} config A button config or array of configs
30368      * @return {Roo.Toolbar.Button/Array}
30369      */
30370     addButton : function(config){
30371         if(config instanceof Array){
30372             var buttons = [];
30373             for(var i = 0, len = config.length; i < len; i++) {
30374                 buttons.push(this.addButton(config[i]));
30375             }
30376             return buttons;
30377         }
30378         var b = config;
30379         if(!(config instanceof Roo.Toolbar.Button)){
30380             b = config.split ?
30381                 new Roo.Toolbar.SplitButton(config) :
30382                 new Roo.Toolbar.Button(config);
30383         }
30384         var td = this.nextBlock();
30385         b.render(td);
30386         this.items.add(b);
30387         return b;
30388     },
30389     
30390     /**
30391      * Adds text to the toolbar
30392      * @param {String} text The text to add
30393      * @return {Roo.Toolbar.Item} The element's item
30394      */
30395     addText : function(text){
30396         return this.addItem(new Roo.Toolbar.TextItem(text));
30397     },
30398     
30399     /**
30400      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30401      * @param {Number} index The index where the item is to be inserted
30402      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30403      * @return {Roo.Toolbar.Button/Item}
30404      */
30405     insertButton : function(index, item){
30406         if(item instanceof Array){
30407             var buttons = [];
30408             for(var i = 0, len = item.length; i < len; i++) {
30409                buttons.push(this.insertButton(index + i, item[i]));
30410             }
30411             return buttons;
30412         }
30413         if (!(item instanceof Roo.Toolbar.Button)){
30414            item = new Roo.Toolbar.Button(item);
30415         }
30416         var td = document.createElement("td");
30417         this.tr.insertBefore(td, this.tr.childNodes[index]);
30418         item.render(td);
30419         this.items.insert(index, item);
30420         return item;
30421     },
30422     
30423     /**
30424      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30425      * @param {Object} config
30426      * @return {Roo.Toolbar.Item} The element's item
30427      */
30428     addDom : function(config, returnEl){
30429         var td = this.nextBlock();
30430         Roo.DomHelper.overwrite(td, config);
30431         var ti = new Roo.Toolbar.Item(td.firstChild);
30432         ti.render(td);
30433         this.items.add(ti);
30434         return ti;
30435     },
30436
30437     /**
30438      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30439      * @type Roo.util.MixedCollection  
30440      */
30441     fields : false,
30442     
30443     /**
30444      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30445      * Note: the field should not have been rendered yet. For a field that has already been
30446      * rendered, use {@link #addElement}.
30447      * @param {Roo.form.Field} field
30448      * @return {Roo.ToolbarItem}
30449      */
30450      
30451       
30452     addField : function(field) {
30453         if (!this.fields) {
30454             var autoId = 0;
30455             this.fields = new Roo.util.MixedCollection(false, function(o){
30456                 return o.id || ("item" + (++autoId));
30457             });
30458
30459         }
30460         
30461         var td = this.nextBlock();
30462         field.render(td);
30463         var ti = new Roo.Toolbar.Item(td.firstChild);
30464         ti.render(td);
30465         this.items.add(ti);
30466         this.fields.add(field);
30467         return ti;
30468     },
30469     /**
30470      * Hide the toolbar
30471      * @method hide
30472      */
30473      
30474       
30475     hide : function()
30476     {
30477         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30478         this.el.child('div').hide();
30479     },
30480     /**
30481      * Show the toolbar
30482      * @method show
30483      */
30484     show : function()
30485     {
30486         this.el.child('div').show();
30487     },
30488       
30489     // private
30490     nextBlock : function(){
30491         var td = document.createElement("td");
30492         this.tr.appendChild(td);
30493         return td;
30494     },
30495
30496     // private
30497     destroy : function(){
30498         if(this.items){ // rendered?
30499             Roo.destroy.apply(Roo, this.items.items);
30500         }
30501         if(this.fields){ // rendered?
30502             Roo.destroy.apply(Roo, this.fields.items);
30503         }
30504         Roo.Element.uncache(this.el, this.tr);
30505     }
30506 };
30507
30508 /**
30509  * @class Roo.Toolbar.Item
30510  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30511  * @constructor
30512  * Creates a new Item
30513  * @param {HTMLElement} el 
30514  */
30515 Roo.Toolbar.Item = function(el){
30516     var cfg = {};
30517     if (typeof (el.xtype) != 'undefined') {
30518         cfg = el;
30519         el = cfg.el;
30520     }
30521     
30522     this.el = Roo.getDom(el);
30523     this.id = Roo.id(this.el);
30524     this.hidden = false;
30525     
30526     this.addEvents({
30527          /**
30528              * @event render
30529              * Fires when the button is rendered
30530              * @param {Button} this
30531              */
30532         'render': true
30533     });
30534     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30535 };
30536 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30537 //Roo.Toolbar.Item.prototype = {
30538     
30539     /**
30540      * Get this item's HTML Element
30541      * @return {HTMLElement}
30542      */
30543     getEl : function(){
30544        return this.el;  
30545     },
30546
30547     // private
30548     render : function(td){
30549         
30550          this.td = td;
30551         td.appendChild(this.el);
30552         
30553         this.fireEvent('render', this);
30554     },
30555     
30556     /**
30557      * Removes and destroys this item.
30558      */
30559     destroy : function(){
30560         this.td.parentNode.removeChild(this.td);
30561     },
30562     
30563     /**
30564      * Shows this item.
30565      */
30566     show: function(){
30567         this.hidden = false;
30568         this.td.style.display = "";
30569     },
30570     
30571     /**
30572      * Hides this item.
30573      */
30574     hide: function(){
30575         this.hidden = true;
30576         this.td.style.display = "none";
30577     },
30578     
30579     /**
30580      * Convenience function for boolean show/hide.
30581      * @param {Boolean} visible true to show/false to hide
30582      */
30583     setVisible: function(visible){
30584         if(visible) {
30585             this.show();
30586         }else{
30587             this.hide();
30588         }
30589     },
30590     
30591     /**
30592      * Try to focus this item.
30593      */
30594     focus : function(){
30595         Roo.fly(this.el).focus();
30596     },
30597     
30598     /**
30599      * Disables this item.
30600      */
30601     disable : function(){
30602         Roo.fly(this.td).addClass("x-item-disabled");
30603         this.disabled = true;
30604         this.el.disabled = true;
30605     },
30606     
30607     /**
30608      * Enables this item.
30609      */
30610     enable : function(){
30611         Roo.fly(this.td).removeClass("x-item-disabled");
30612         this.disabled = false;
30613         this.el.disabled = false;
30614     }
30615 });
30616
30617
30618 /**
30619  * @class Roo.Toolbar.Separator
30620  * @extends Roo.Toolbar.Item
30621  * A simple toolbar separator class
30622  * @constructor
30623  * Creates a new Separator
30624  */
30625 Roo.Toolbar.Separator = function(cfg){
30626     
30627     var s = document.createElement("span");
30628     s.className = "ytb-sep";
30629     if (cfg) {
30630         cfg.el = s;
30631     }
30632     
30633     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30634 };
30635 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30636     enable:Roo.emptyFn,
30637     disable:Roo.emptyFn,
30638     focus:Roo.emptyFn
30639 });
30640
30641 /**
30642  * @class Roo.Toolbar.Spacer
30643  * @extends Roo.Toolbar.Item
30644  * A simple element that adds extra horizontal space to a toolbar.
30645  * @constructor
30646  * Creates a new Spacer
30647  */
30648 Roo.Toolbar.Spacer = function(cfg){
30649     var s = document.createElement("div");
30650     s.className = "ytb-spacer";
30651     if (cfg) {
30652         cfg.el = s;
30653     }
30654     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30655 };
30656 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30657     enable:Roo.emptyFn,
30658     disable:Roo.emptyFn,
30659     focus:Roo.emptyFn
30660 });
30661
30662 /**
30663  * @class Roo.Toolbar.Fill
30664  * @extends Roo.Toolbar.Spacer
30665  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30666  * @constructor
30667  * Creates a new Spacer
30668  */
30669 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30670     // private
30671     render : function(td){
30672         td.style.width = '100%';
30673         Roo.Toolbar.Fill.superclass.render.call(this, td);
30674     }
30675 });
30676
30677 /**
30678  * @class Roo.Toolbar.TextItem
30679  * @extends Roo.Toolbar.Item
30680  * A simple class that renders text directly into a toolbar.
30681  * @constructor
30682  * Creates a new TextItem
30683  * @cfg {string} text 
30684  */
30685 Roo.Toolbar.TextItem = function(cfg){
30686     var  text = cfg || "";
30687     if (typeof(cfg) == 'object') {
30688         text = cfg.text || "";
30689     }  else {
30690         cfg = null;
30691     }
30692     var s = document.createElement("span");
30693     s.className = "ytb-text";
30694     s.innerHTML = text;
30695     if (cfg) {
30696         cfg.el  = s;
30697     }
30698     
30699     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30700 };
30701 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30702     
30703      
30704     enable:Roo.emptyFn,
30705     disable:Roo.emptyFn,
30706     focus:Roo.emptyFn
30707 });
30708
30709 /**
30710  * @class Roo.Toolbar.Button
30711  * @extends Roo.Button
30712  * A button that renders into a toolbar.
30713  * @constructor
30714  * Creates a new Button
30715  * @param {Object} config A standard {@link Roo.Button} config object
30716  */
30717 Roo.Toolbar.Button = function(config){
30718     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30719 };
30720 Roo.extend(Roo.Toolbar.Button, Roo.Button,
30721 {
30722     
30723     
30724     render : function(td){
30725         this.td = td;
30726         Roo.Toolbar.Button.superclass.render.call(this, td);
30727     },
30728     
30729     /**
30730      * Removes and destroys this button
30731      */
30732     destroy : function(){
30733         Roo.Toolbar.Button.superclass.destroy.call(this);
30734         this.td.parentNode.removeChild(this.td);
30735     },
30736     
30737     /**
30738      * Shows this button
30739      */
30740     show: function(){
30741         this.hidden = false;
30742         this.td.style.display = "";
30743     },
30744     
30745     /**
30746      * Hides this button
30747      */
30748     hide: function(){
30749         this.hidden = true;
30750         this.td.style.display = "none";
30751     },
30752
30753     /**
30754      * Disables this item
30755      */
30756     disable : function(){
30757         Roo.fly(this.td).addClass("x-item-disabled");
30758         this.disabled = true;
30759     },
30760
30761     /**
30762      * Enables this item
30763      */
30764     enable : function(){
30765         Roo.fly(this.td).removeClass("x-item-disabled");
30766         this.disabled = false;
30767     }
30768 });
30769 // backwards compat
30770 Roo.ToolbarButton = Roo.Toolbar.Button;
30771
30772 /**
30773  * @class Roo.Toolbar.SplitButton
30774  * @extends Roo.SplitButton
30775  * A menu button that renders into a toolbar.
30776  * @constructor
30777  * Creates a new SplitButton
30778  * @param {Object} config A standard {@link Roo.SplitButton} config object
30779  */
30780 Roo.Toolbar.SplitButton = function(config){
30781     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30782 };
30783 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30784     render : function(td){
30785         this.td = td;
30786         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30787     },
30788     
30789     /**
30790      * Removes and destroys this button
30791      */
30792     destroy : function(){
30793         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30794         this.td.parentNode.removeChild(this.td);
30795     },
30796     
30797     /**
30798      * Shows this button
30799      */
30800     show: function(){
30801         this.hidden = false;
30802         this.td.style.display = "";
30803     },
30804     
30805     /**
30806      * Hides this button
30807      */
30808     hide: function(){
30809         this.hidden = true;
30810         this.td.style.display = "none";
30811     }
30812 });
30813
30814 // backwards compat
30815 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30816  * Based on:
30817  * Ext JS Library 1.1.1
30818  * Copyright(c) 2006-2007, Ext JS, LLC.
30819  *
30820  * Originally Released Under LGPL - original licence link has changed is not relivant.
30821  *
30822  * Fork - LGPL
30823  * <script type="text/javascript">
30824  */
30825  
30826 /**
30827  * @class Roo.PagingToolbar
30828  * @extends Roo.Toolbar
30829  * @children   Roo.Toolbar.Item Roo.form.Field
30830  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30831  * @constructor
30832  * Create a new PagingToolbar
30833  * @param {Object} config The config object
30834  */
30835 Roo.PagingToolbar = function(el, ds, config)
30836 {
30837     // old args format still supported... - xtype is prefered..
30838     if (typeof(el) == 'object' && el.xtype) {
30839         // created from xtype...
30840         config = el;
30841         ds = el.dataSource;
30842         el = config.container;
30843     }
30844     var items = [];
30845     if (config.items) {
30846         items = config.items;
30847         config.items = [];
30848     }
30849     
30850     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30851     this.ds = ds;
30852     this.cursor = 0;
30853     this.renderButtons(this.el);
30854     this.bind(ds);
30855     
30856     // supprot items array.
30857    
30858     Roo.each(items, function(e) {
30859         this.add(Roo.factory(e));
30860     },this);
30861     
30862 };
30863
30864 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30865    
30866     /**
30867      * @cfg {String/HTMLElement/Element} container
30868      * container The id or element that will contain the toolbar
30869      */
30870     /**
30871      * @cfg {Boolean} displayInfo
30872      * True to display the displayMsg (defaults to false)
30873      */
30874     
30875     
30876     /**
30877      * @cfg {Number} pageSize
30878      * The number of records to display per page (defaults to 20)
30879      */
30880     pageSize: 20,
30881     /**
30882      * @cfg {String} displayMsg
30883      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30884      */
30885     displayMsg : 'Displaying {0} - {1} of {2}',
30886     /**
30887      * @cfg {String} emptyMsg
30888      * The message to display when no records are found (defaults to "No data to display")
30889      */
30890     emptyMsg : 'No data to display',
30891     /**
30892      * Customizable piece of the default paging text (defaults to "Page")
30893      * @type String
30894      */
30895     beforePageText : "Page",
30896     /**
30897      * Customizable piece of the default paging text (defaults to "of %0")
30898      * @type String
30899      */
30900     afterPageText : "of {0}",
30901     /**
30902      * Customizable piece of the default paging text (defaults to "First Page")
30903      * @type String
30904      */
30905     firstText : "First Page",
30906     /**
30907      * Customizable piece of the default paging text (defaults to "Previous Page")
30908      * @type String
30909      */
30910     prevText : "Previous Page",
30911     /**
30912      * Customizable piece of the default paging text (defaults to "Next Page")
30913      * @type String
30914      */
30915     nextText : "Next Page",
30916     /**
30917      * Customizable piece of the default paging text (defaults to "Last Page")
30918      * @type String
30919      */
30920     lastText : "Last Page",
30921     /**
30922      * Customizable piece of the default paging text (defaults to "Refresh")
30923      * @type String
30924      */
30925     refreshText : "Refresh",
30926
30927     // private
30928     renderButtons : function(el){
30929         Roo.PagingToolbar.superclass.render.call(this, el);
30930         this.first = this.addButton({
30931             tooltip: this.firstText,
30932             cls: "x-btn-icon x-grid-page-first",
30933             disabled: true,
30934             handler: this.onClick.createDelegate(this, ["first"])
30935         });
30936         this.prev = this.addButton({
30937             tooltip: this.prevText,
30938             cls: "x-btn-icon x-grid-page-prev",
30939             disabled: true,
30940             handler: this.onClick.createDelegate(this, ["prev"])
30941         });
30942         //this.addSeparator();
30943         this.add(this.beforePageText);
30944         this.field = Roo.get(this.addDom({
30945            tag: "input",
30946            type: "text",
30947            size: "3",
30948            value: "1",
30949            cls: "x-grid-page-number"
30950         }).el);
30951         this.field.on("keydown", this.onPagingKeydown, this);
30952         this.field.on("focus", function(){this.dom.select();});
30953         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30954         this.field.setHeight(18);
30955         //this.addSeparator();
30956         this.next = this.addButton({
30957             tooltip: this.nextText,
30958             cls: "x-btn-icon x-grid-page-next",
30959             disabled: true,
30960             handler: this.onClick.createDelegate(this, ["next"])
30961         });
30962         this.last = this.addButton({
30963             tooltip: this.lastText,
30964             cls: "x-btn-icon x-grid-page-last",
30965             disabled: true,
30966             handler: this.onClick.createDelegate(this, ["last"])
30967         });
30968         //this.addSeparator();
30969         this.loading = this.addButton({
30970             tooltip: this.refreshText,
30971             cls: "x-btn-icon x-grid-loading",
30972             handler: this.onClick.createDelegate(this, ["refresh"])
30973         });
30974
30975         if(this.displayInfo){
30976             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30977         }
30978     },
30979
30980     // private
30981     updateInfo : function(){
30982         if(this.displayEl){
30983             var count = this.ds.getCount();
30984             var msg = count == 0 ?
30985                 this.emptyMsg :
30986                 String.format(
30987                     this.displayMsg,
30988                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30989                 );
30990             this.displayEl.update(msg);
30991         }
30992     },
30993
30994     // private
30995     onLoad : function(ds, r, o){
30996        this.cursor = o.params ? o.params.start : 0;
30997        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30998
30999        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31000        this.field.dom.value = ap;
31001        this.first.setDisabled(ap == 1);
31002        this.prev.setDisabled(ap == 1);
31003        this.next.setDisabled(ap == ps);
31004        this.last.setDisabled(ap == ps);
31005        this.loading.enable();
31006        this.updateInfo();
31007     },
31008
31009     // private
31010     getPageData : function(){
31011         var total = this.ds.getTotalCount();
31012         return {
31013             total : total,
31014             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31015             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31016         };
31017     },
31018
31019     // private
31020     onLoadError : function(){
31021         this.loading.enable();
31022     },
31023
31024     // private
31025     onPagingKeydown : function(e){
31026         var k = e.getKey();
31027         var d = this.getPageData();
31028         if(k == e.RETURN){
31029             var v = this.field.dom.value, pageNum;
31030             if(!v || isNaN(pageNum = parseInt(v, 10))){
31031                 this.field.dom.value = d.activePage;
31032                 return;
31033             }
31034             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31035             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31036             e.stopEvent();
31037         }
31038         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))
31039         {
31040           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31041           this.field.dom.value = pageNum;
31042           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31043           e.stopEvent();
31044         }
31045         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31046         {
31047           var v = this.field.dom.value, pageNum; 
31048           var increment = (e.shiftKey) ? 10 : 1;
31049           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31050             increment *= -1;
31051           }
31052           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31053             this.field.dom.value = d.activePage;
31054             return;
31055           }
31056           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31057           {
31058             this.field.dom.value = parseInt(v, 10) + increment;
31059             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31060             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31061           }
31062           e.stopEvent();
31063         }
31064     },
31065
31066     // private
31067     beforeLoad : function(){
31068         if(this.loading){
31069             this.loading.disable();
31070         }
31071     },
31072
31073     // private
31074     onClick : function(which){
31075         var ds = this.ds;
31076         switch(which){
31077             case "first":
31078                 ds.load({params:{start: 0, limit: this.pageSize}});
31079             break;
31080             case "prev":
31081                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31082             break;
31083             case "next":
31084                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31085             break;
31086             case "last":
31087                 var total = ds.getTotalCount();
31088                 var extra = total % this.pageSize;
31089                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31090                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31091             break;
31092             case "refresh":
31093                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31094             break;
31095         }
31096     },
31097
31098     /**
31099      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31100      * @param {Roo.data.Store} store The data store to unbind
31101      */
31102     unbind : function(ds){
31103         ds.un("beforeload", this.beforeLoad, this);
31104         ds.un("load", this.onLoad, this);
31105         ds.un("loadexception", this.onLoadError, this);
31106         ds.un("remove", this.updateInfo, this);
31107         ds.un("add", this.updateInfo, this);
31108         this.ds = undefined;
31109     },
31110
31111     /**
31112      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31113      * @param {Roo.data.Store} store The data store to bind
31114      */
31115     bind : function(ds){
31116         ds.on("beforeload", this.beforeLoad, this);
31117         ds.on("load", this.onLoad, this);
31118         ds.on("loadexception", this.onLoadError, this);
31119         ds.on("remove", this.updateInfo, this);
31120         ds.on("add", this.updateInfo, this);
31121         this.ds = ds;
31122     }
31123 });/*
31124  * Based on:
31125  * Ext JS Library 1.1.1
31126  * Copyright(c) 2006-2007, Ext JS, LLC.
31127  *
31128  * Originally Released Under LGPL - original licence link has changed is not relivant.
31129  *
31130  * Fork - LGPL
31131  * <script type="text/javascript">
31132  */
31133
31134 /**
31135  * @class Roo.Resizable
31136  * @extends Roo.util.Observable
31137  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31138  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31139  * 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
31140  * the element will be wrapped for you automatically.</p>
31141  * <p>Here is the list of valid resize handles:</p>
31142  * <pre>
31143 Value   Description
31144 ------  -------------------
31145  'n'     north
31146  's'     south
31147  'e'     east
31148  'w'     west
31149  'nw'    northwest
31150  'sw'    southwest
31151  'se'    southeast
31152  'ne'    northeast
31153  'hd'    horizontal drag
31154  'all'   all
31155 </pre>
31156  * <p>Here's an example showing the creation of a typical Resizable:</p>
31157  * <pre><code>
31158 var resizer = new Roo.Resizable("element-id", {
31159     handles: 'all',
31160     minWidth: 200,
31161     minHeight: 100,
31162     maxWidth: 500,
31163     maxHeight: 400,
31164     pinned: true
31165 });
31166 resizer.on("resize", myHandler);
31167 </code></pre>
31168  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31169  * resizer.east.setDisplayed(false);</p>
31170  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31171  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31172  * resize operation's new size (defaults to [0, 0])
31173  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31174  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31175  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31176  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31177  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31178  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31179  * @cfg {Number} width The width of the element in pixels (defaults to null)
31180  * @cfg {Number} height The height of the element in pixels (defaults to null)
31181  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31182  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31183  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31184  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31185  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31186  * in favor of the handles config option (defaults to false)
31187  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31188  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31189  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31190  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31191  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31192  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31193  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31194  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31195  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31196  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31197  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31198  * @constructor
31199  * Create a new resizable component
31200  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31201  * @param {Object} config configuration options
31202   */
31203 Roo.Resizable = function(el, config)
31204 {
31205     this.el = Roo.get(el);
31206
31207     if(config && config.wrap){
31208         config.resizeChild = this.el;
31209         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31210         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31211         this.el.setStyle("overflow", "hidden");
31212         this.el.setPositioning(config.resizeChild.getPositioning());
31213         config.resizeChild.clearPositioning();
31214         if(!config.width || !config.height){
31215             var csize = config.resizeChild.getSize();
31216             this.el.setSize(csize.width, csize.height);
31217         }
31218         if(config.pinned && !config.adjustments){
31219             config.adjustments = "auto";
31220         }
31221     }
31222
31223     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31224     this.proxy.unselectable();
31225     this.proxy.enableDisplayMode('block');
31226
31227     Roo.apply(this, config);
31228
31229     if(this.pinned){
31230         this.disableTrackOver = true;
31231         this.el.addClass("x-resizable-pinned");
31232     }
31233     // if the element isn't positioned, make it relative
31234     var position = this.el.getStyle("position");
31235     if(position != "absolute" && position != "fixed"){
31236         this.el.setStyle("position", "relative");
31237     }
31238     if(!this.handles){ // no handles passed, must be legacy style
31239         this.handles = 's,e,se';
31240         if(this.multiDirectional){
31241             this.handles += ',n,w';
31242         }
31243     }
31244     if(this.handles == "all"){
31245         this.handles = "n s e w ne nw se sw";
31246     }
31247     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31248     var ps = Roo.Resizable.positions;
31249     for(var i = 0, len = hs.length; i < len; i++){
31250         if(hs[i] && ps[hs[i]]){
31251             var pos = ps[hs[i]];
31252             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31253         }
31254     }
31255     // legacy
31256     this.corner = this.southeast;
31257     
31258     // updateBox = the box can move..
31259     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31260         this.updateBox = true;
31261     }
31262
31263     this.activeHandle = null;
31264
31265     if(this.resizeChild){
31266         if(typeof this.resizeChild == "boolean"){
31267             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31268         }else{
31269             this.resizeChild = Roo.get(this.resizeChild, true);
31270         }
31271     }
31272     
31273     if(this.adjustments == "auto"){
31274         var rc = this.resizeChild;
31275         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31276         if(rc && (hw || hn)){
31277             rc.position("relative");
31278             rc.setLeft(hw ? hw.el.getWidth() : 0);
31279             rc.setTop(hn ? hn.el.getHeight() : 0);
31280         }
31281         this.adjustments = [
31282             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31283             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31284         ];
31285     }
31286
31287     if(this.draggable){
31288         this.dd = this.dynamic ?
31289             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31290         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31291     }
31292
31293     // public events
31294     this.addEvents({
31295         /**
31296          * @event beforeresize
31297          * Fired before resize is allowed. Set enabled to false to cancel resize.
31298          * @param {Roo.Resizable} this
31299          * @param {Roo.EventObject} e The mousedown event
31300          */
31301         "beforeresize" : true,
31302         /**
31303          * @event resizing
31304          * Fired a resizing.
31305          * @param {Roo.Resizable} this
31306          * @param {Number} x The new x position
31307          * @param {Number} y The new y position
31308          * @param {Number} w The new w width
31309          * @param {Number} h The new h hight
31310          * @param {Roo.EventObject} e The mouseup event
31311          */
31312         "resizing" : true,
31313         /**
31314          * @event resize
31315          * Fired after a resize.
31316          * @param {Roo.Resizable} this
31317          * @param {Number} width The new width
31318          * @param {Number} height The new height
31319          * @param {Roo.EventObject} e The mouseup event
31320          */
31321         "resize" : true
31322     });
31323
31324     if(this.width !== null && this.height !== null){
31325         this.resizeTo(this.width, this.height);
31326     }else{
31327         this.updateChildSize();
31328     }
31329     if(Roo.isIE){
31330         this.el.dom.style.zoom = 1;
31331     }
31332     Roo.Resizable.superclass.constructor.call(this);
31333 };
31334
31335 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31336         resizeChild : false,
31337         adjustments : [0, 0],
31338         minWidth : 5,
31339         minHeight : 5,
31340         maxWidth : 10000,
31341         maxHeight : 10000,
31342         enabled : true,
31343         animate : false,
31344         duration : .35,
31345         dynamic : false,
31346         handles : false,
31347         multiDirectional : false,
31348         disableTrackOver : false,
31349         easing : 'easeOutStrong',
31350         widthIncrement : 0,
31351         heightIncrement : 0,
31352         pinned : false,
31353         width : null,
31354         height : null,
31355         preserveRatio : false,
31356         transparent: false,
31357         minX: 0,
31358         minY: 0,
31359         draggable: false,
31360
31361         /**
31362          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31363          */
31364         constrainTo: undefined,
31365         /**
31366          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31367          */
31368         resizeRegion: undefined,
31369
31370
31371     /**
31372      * Perform a manual resize
31373      * @param {Number} width
31374      * @param {Number} height
31375      */
31376     resizeTo : function(width, height){
31377         this.el.setSize(width, height);
31378         this.updateChildSize();
31379         this.fireEvent("resize", this, width, height, null);
31380     },
31381
31382     // private
31383     startSizing : function(e, handle){
31384         this.fireEvent("beforeresize", this, e);
31385         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31386
31387             if(!this.overlay){
31388                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31389                 this.overlay.unselectable();
31390                 this.overlay.enableDisplayMode("block");
31391                 this.overlay.on("mousemove", this.onMouseMove, this);
31392                 this.overlay.on("mouseup", this.onMouseUp, this);
31393             }
31394             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31395
31396             this.resizing = true;
31397             this.startBox = this.el.getBox();
31398             this.startPoint = e.getXY();
31399             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31400                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31401
31402             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31403             this.overlay.show();
31404
31405             if(this.constrainTo) {
31406                 var ct = Roo.get(this.constrainTo);
31407                 this.resizeRegion = ct.getRegion().adjust(
31408                     ct.getFrameWidth('t'),
31409                     ct.getFrameWidth('l'),
31410                     -ct.getFrameWidth('b'),
31411                     -ct.getFrameWidth('r')
31412                 );
31413             }
31414
31415             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31416             this.proxy.show();
31417             this.proxy.setBox(this.startBox);
31418             if(!this.dynamic){
31419                 this.proxy.setStyle('visibility', 'visible');
31420             }
31421         }
31422     },
31423
31424     // private
31425     onMouseDown : function(handle, e){
31426         if(this.enabled){
31427             e.stopEvent();
31428             this.activeHandle = handle;
31429             this.startSizing(e, handle);
31430         }
31431     },
31432
31433     // private
31434     onMouseUp : function(e){
31435         var size = this.resizeElement();
31436         this.resizing = false;
31437         this.handleOut();
31438         this.overlay.hide();
31439         this.proxy.hide();
31440         this.fireEvent("resize", this, size.width, size.height, e);
31441     },
31442
31443     // private
31444     updateChildSize : function(){
31445         
31446         if(this.resizeChild){
31447             var el = this.el;
31448             var child = this.resizeChild;
31449             var adj = this.adjustments;
31450             if(el.dom.offsetWidth){
31451                 var b = el.getSize(true);
31452                 child.setSize(b.width+adj[0], b.height+adj[1]);
31453             }
31454             // Second call here for IE
31455             // The first call enables instant resizing and
31456             // the second call corrects scroll bars if they
31457             // exist
31458             if(Roo.isIE){
31459                 setTimeout(function(){
31460                     if(el.dom.offsetWidth){
31461                         var b = el.getSize(true);
31462                         child.setSize(b.width+adj[0], b.height+adj[1]);
31463                     }
31464                 }, 10);
31465             }
31466         }
31467     },
31468
31469     // private
31470     snap : function(value, inc, min){
31471         if(!inc || !value) {
31472             return value;
31473         }
31474         var newValue = value;
31475         var m = value % inc;
31476         if(m > 0){
31477             if(m > (inc/2)){
31478                 newValue = value + (inc-m);
31479             }else{
31480                 newValue = value - m;
31481             }
31482         }
31483         return Math.max(min, newValue);
31484     },
31485
31486     // private
31487     resizeElement : function(){
31488         var box = this.proxy.getBox();
31489         if(this.updateBox){
31490             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31491         }else{
31492             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31493         }
31494         this.updateChildSize();
31495         if(!this.dynamic){
31496             this.proxy.hide();
31497         }
31498         return box;
31499     },
31500
31501     // private
31502     constrain : function(v, diff, m, mx){
31503         if(v - diff < m){
31504             diff = v - m;
31505         }else if(v - diff > mx){
31506             diff = mx - v;
31507         }
31508         return diff;
31509     },
31510
31511     // private
31512     onMouseMove : function(e){
31513         
31514         if(this.enabled){
31515             try{// try catch so if something goes wrong the user doesn't get hung
31516
31517             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31518                 return;
31519             }
31520
31521             //var curXY = this.startPoint;
31522             var curSize = this.curSize || this.startBox;
31523             var x = this.startBox.x, y = this.startBox.y;
31524             var ox = x, oy = y;
31525             var w = curSize.width, h = curSize.height;
31526             var ow = w, oh = h;
31527             var mw = this.minWidth, mh = this.minHeight;
31528             var mxw = this.maxWidth, mxh = this.maxHeight;
31529             var wi = this.widthIncrement;
31530             var hi = this.heightIncrement;
31531
31532             var eventXY = e.getXY();
31533             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31534             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31535
31536             var pos = this.activeHandle.position;
31537
31538             switch(pos){
31539                 case "east":
31540                     w += diffX;
31541                     w = Math.min(Math.max(mw, w), mxw);
31542                     break;
31543              
31544                 case "south":
31545                     h += diffY;
31546                     h = Math.min(Math.max(mh, h), mxh);
31547                     break;
31548                 case "southeast":
31549                     w += diffX;
31550                     h += diffY;
31551                     w = Math.min(Math.max(mw, w), mxw);
31552                     h = Math.min(Math.max(mh, h), mxh);
31553                     break;
31554                 case "north":
31555                     diffY = this.constrain(h, diffY, mh, mxh);
31556                     y += diffY;
31557                     h -= diffY;
31558                     break;
31559                 case "hdrag":
31560                     
31561                     if (wi) {
31562                         var adiffX = Math.abs(diffX);
31563                         var sub = (adiffX % wi); // how much 
31564                         if (sub > (wi/2)) { // far enough to snap
31565                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31566                         } else {
31567                             // remove difference.. 
31568                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31569                         }
31570                     }
31571                     x += diffX;
31572                     x = Math.max(this.minX, x);
31573                     break;
31574                 case "west":
31575                     diffX = this.constrain(w, diffX, mw, mxw);
31576                     x += diffX;
31577                     w -= diffX;
31578                     break;
31579                 case "northeast":
31580                     w += diffX;
31581                     w = Math.min(Math.max(mw, w), mxw);
31582                     diffY = this.constrain(h, diffY, mh, mxh);
31583                     y += diffY;
31584                     h -= diffY;
31585                     break;
31586                 case "northwest":
31587                     diffX = this.constrain(w, diffX, mw, mxw);
31588                     diffY = this.constrain(h, diffY, mh, mxh);
31589                     y += diffY;
31590                     h -= diffY;
31591                     x += diffX;
31592                     w -= diffX;
31593                     break;
31594                case "southwest":
31595                     diffX = this.constrain(w, diffX, mw, mxw);
31596                     h += diffY;
31597                     h = Math.min(Math.max(mh, h), mxh);
31598                     x += diffX;
31599                     w -= diffX;
31600                     break;
31601             }
31602
31603             var sw = this.snap(w, wi, mw);
31604             var sh = this.snap(h, hi, mh);
31605             if(sw != w || sh != h){
31606                 switch(pos){
31607                     case "northeast":
31608                         y -= sh - h;
31609                     break;
31610                     case "north":
31611                         y -= sh - h;
31612                         break;
31613                     case "southwest":
31614                         x -= sw - w;
31615                     break;
31616                     case "west":
31617                         x -= sw - w;
31618                         break;
31619                     case "northwest":
31620                         x -= sw - w;
31621                         y -= sh - h;
31622                     break;
31623                 }
31624                 w = sw;
31625                 h = sh;
31626             }
31627
31628             if(this.preserveRatio){
31629                 switch(pos){
31630                     case "southeast":
31631                     case "east":
31632                         h = oh * (w/ow);
31633                         h = Math.min(Math.max(mh, h), mxh);
31634                         w = ow * (h/oh);
31635                        break;
31636                     case "south":
31637                         w = ow * (h/oh);
31638                         w = Math.min(Math.max(mw, w), mxw);
31639                         h = oh * (w/ow);
31640                         break;
31641                     case "northeast":
31642                         w = ow * (h/oh);
31643                         w = Math.min(Math.max(mw, w), mxw);
31644                         h = oh * (w/ow);
31645                     break;
31646                     case "north":
31647                         var tw = w;
31648                         w = ow * (h/oh);
31649                         w = Math.min(Math.max(mw, w), mxw);
31650                         h = oh * (w/ow);
31651                         x += (tw - w) / 2;
31652                         break;
31653                     case "southwest":
31654                         h = oh * (w/ow);
31655                         h = Math.min(Math.max(mh, h), mxh);
31656                         var tw = w;
31657                         w = ow * (h/oh);
31658                         x += tw - w;
31659                         break;
31660                     case "west":
31661                         var th = h;
31662                         h = oh * (w/ow);
31663                         h = Math.min(Math.max(mh, h), mxh);
31664                         y += (th - h) / 2;
31665                         var tw = w;
31666                         w = ow * (h/oh);
31667                         x += tw - w;
31668                        break;
31669                     case "northwest":
31670                         var tw = w;
31671                         var th = h;
31672                         h = oh * (w/ow);
31673                         h = Math.min(Math.max(mh, h), mxh);
31674                         w = ow * (h/oh);
31675                         y += th - h;
31676                         x += tw - w;
31677                        break;
31678
31679                 }
31680             }
31681             if (pos == 'hdrag') {
31682                 w = ow;
31683             }
31684             this.proxy.setBounds(x, y, w, h);
31685             if(this.dynamic){
31686                 this.resizeElement();
31687             }
31688             }catch(e){}
31689         }
31690         this.fireEvent("resizing", this, x, y, w, h, e);
31691     },
31692
31693     // private
31694     handleOver : function(){
31695         if(this.enabled){
31696             this.el.addClass("x-resizable-over");
31697         }
31698     },
31699
31700     // private
31701     handleOut : function(){
31702         if(!this.resizing){
31703             this.el.removeClass("x-resizable-over");
31704         }
31705     },
31706
31707     /**
31708      * Returns the element this component is bound to.
31709      * @return {Roo.Element}
31710      */
31711     getEl : function(){
31712         return this.el;
31713     },
31714
31715     /**
31716      * Returns the resizeChild element (or null).
31717      * @return {Roo.Element}
31718      */
31719     getResizeChild : function(){
31720         return this.resizeChild;
31721     },
31722     groupHandler : function()
31723     {
31724         
31725     },
31726     /**
31727      * Destroys this resizable. If the element was wrapped and
31728      * removeEl is not true then the element remains.
31729      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31730      */
31731     destroy : function(removeEl){
31732         this.proxy.remove();
31733         if(this.overlay){
31734             this.overlay.removeAllListeners();
31735             this.overlay.remove();
31736         }
31737         var ps = Roo.Resizable.positions;
31738         for(var k in ps){
31739             if(typeof ps[k] != "function" && this[ps[k]]){
31740                 var h = this[ps[k]];
31741                 h.el.removeAllListeners();
31742                 h.el.remove();
31743             }
31744         }
31745         if(removeEl){
31746             this.el.update("");
31747             this.el.remove();
31748         }
31749     }
31750 });
31751
31752 // private
31753 // hash to map config positions to true positions
31754 Roo.Resizable.positions = {
31755     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31756     hd: "hdrag"
31757 };
31758
31759 // private
31760 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31761     if(!this.tpl){
31762         // only initialize the template if resizable is used
31763         var tpl = Roo.DomHelper.createTemplate(
31764             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31765         );
31766         tpl.compile();
31767         Roo.Resizable.Handle.prototype.tpl = tpl;
31768     }
31769     this.position = pos;
31770     this.rz = rz;
31771     // show north drag fro topdra
31772     var handlepos = pos == 'hdrag' ? 'north' : pos;
31773     
31774     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31775     if (pos == 'hdrag') {
31776         this.el.setStyle('cursor', 'pointer');
31777     }
31778     this.el.unselectable();
31779     if(transparent){
31780         this.el.setOpacity(0);
31781     }
31782     this.el.on("mousedown", this.onMouseDown, this);
31783     if(!disableTrackOver){
31784         this.el.on("mouseover", this.onMouseOver, this);
31785         this.el.on("mouseout", this.onMouseOut, this);
31786     }
31787 };
31788
31789 // private
31790 Roo.Resizable.Handle.prototype = {
31791     afterResize : function(rz){
31792         Roo.log('after?');
31793         // do nothing
31794     },
31795     // private
31796     onMouseDown : function(e){
31797         this.rz.onMouseDown(this, e);
31798     },
31799     // private
31800     onMouseOver : function(e){
31801         this.rz.handleOver(this, e);
31802     },
31803     // private
31804     onMouseOut : function(e){
31805         this.rz.handleOut(this, e);
31806     }
31807 };/*
31808  * Based on:
31809  * Ext JS Library 1.1.1
31810  * Copyright(c) 2006-2007, Ext JS, LLC.
31811  *
31812  * Originally Released Under LGPL - original licence link has changed is not relivant.
31813  *
31814  * Fork - LGPL
31815  * <script type="text/javascript">
31816  */
31817
31818 /**
31819  * @class Roo.Editor
31820  * @extends Roo.Component
31821  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31822  * @constructor
31823  * Create a new Editor
31824  * @param {Roo.form.Field} field The Field object (or descendant)
31825  * @param {Object} config The config object
31826  */
31827 Roo.Editor = function(field, config){
31828     Roo.Editor.superclass.constructor.call(this, config);
31829     this.field = field;
31830     this.addEvents({
31831         /**
31832              * @event beforestartedit
31833              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31834              * false from the handler of this event.
31835              * @param {Editor} this
31836              * @param {Roo.Element} boundEl The underlying element bound to this editor
31837              * @param {Mixed} value The field value being set
31838              */
31839         "beforestartedit" : true,
31840         /**
31841              * @event startedit
31842              * Fires when this editor is displayed
31843              * @param {Roo.Element} boundEl The underlying element bound to this editor
31844              * @param {Mixed} value The starting field value
31845              */
31846         "startedit" : true,
31847         /**
31848              * @event beforecomplete
31849              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31850              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31851              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31852              * event will not fire since no edit actually occurred.
31853              * @param {Editor} this
31854              * @param {Mixed} value The current field value
31855              * @param {Mixed} startValue The original field value
31856              */
31857         "beforecomplete" : true,
31858         /**
31859              * @event complete
31860              * Fires after editing is complete and any changed value has been written to the underlying field.
31861              * @param {Editor} this
31862              * @param {Mixed} value The current field value
31863              * @param {Mixed} startValue The original field value
31864              */
31865         "complete" : true,
31866         /**
31867          * @event specialkey
31868          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31869          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31870          * @param {Roo.form.Field} this
31871          * @param {Roo.EventObject} e The event object
31872          */
31873         "specialkey" : true
31874     });
31875 };
31876
31877 Roo.extend(Roo.Editor, Roo.Component, {
31878     /**
31879      * @cfg {Boolean/String} autosize
31880      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31881      * or "height" to adopt the height only (defaults to false)
31882      */
31883     /**
31884      * @cfg {Boolean} revertInvalid
31885      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31886      * validation fails (defaults to true)
31887      */
31888     /**
31889      * @cfg {Boolean} ignoreNoChange
31890      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31891      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31892      * will never be ignored.
31893      */
31894     /**
31895      * @cfg {Boolean} hideEl
31896      * False to keep the bound element visible while the editor is displayed (defaults to true)
31897      */
31898     /**
31899      * @cfg {Mixed} value
31900      * The data value of the underlying field (defaults to "")
31901      */
31902     value : "",
31903     /**
31904      * @cfg {String} alignment
31905      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31906      */
31907     alignment: "c-c?",
31908     /**
31909      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31910      * for bottom-right shadow (defaults to "frame")
31911      */
31912     shadow : "frame",
31913     /**
31914      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31915      */
31916     constrain : false,
31917     /**
31918      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31919      */
31920     completeOnEnter : false,
31921     /**
31922      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31923      */
31924     cancelOnEsc : false,
31925     /**
31926      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31927      */
31928     updateEl : false,
31929
31930     // private
31931     onRender : function(ct, position){
31932         this.el = new Roo.Layer({
31933             shadow: this.shadow,
31934             cls: "x-editor",
31935             parentEl : ct,
31936             shim : this.shim,
31937             shadowOffset:4,
31938             id: this.id,
31939             constrain: this.constrain
31940         });
31941         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31942         if(this.field.msgTarget != 'title'){
31943             this.field.msgTarget = 'qtip';
31944         }
31945         this.field.render(this.el);
31946         if(Roo.isGecko){
31947             this.field.el.dom.setAttribute('autocomplete', 'off');
31948         }
31949         this.field.on("specialkey", this.onSpecialKey, this);
31950         if(this.swallowKeys){
31951             this.field.el.swallowEvent(['keydown','keypress']);
31952         }
31953         this.field.show();
31954         this.field.on("blur", this.onBlur, this);
31955         if(this.field.grow){
31956             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31957         }
31958     },
31959
31960     onSpecialKey : function(field, e)
31961     {
31962         //Roo.log('editor onSpecialKey');
31963         if(this.completeOnEnter && e.getKey() == e.ENTER){
31964             e.stopEvent();
31965             this.completeEdit();
31966             return;
31967         }
31968         // do not fire special key otherwise it might hide close the editor...
31969         if(e.getKey() == e.ENTER){    
31970             return;
31971         }
31972         if(this.cancelOnEsc && e.getKey() == e.ESC){
31973             this.cancelEdit();
31974             return;
31975         } 
31976         this.fireEvent('specialkey', field, e);
31977     
31978     },
31979
31980     /**
31981      * Starts the editing process and shows the editor.
31982      * @param {String/HTMLElement/Element} el The element to edit
31983      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31984       * to the innerHTML of el.
31985      */
31986     startEdit : function(el, value){
31987         if(this.editing){
31988             this.completeEdit();
31989         }
31990         this.boundEl = Roo.get(el);
31991         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31992         if(!this.rendered){
31993             this.render(this.parentEl || document.body);
31994         }
31995         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31996             return;
31997         }
31998         this.startValue = v;
31999         this.field.setValue(v);
32000         if(this.autoSize){
32001             var sz = this.boundEl.getSize();
32002             switch(this.autoSize){
32003                 case "width":
32004                 this.setSize(sz.width,  "");
32005                 break;
32006                 case "height":
32007                 this.setSize("",  sz.height);
32008                 break;
32009                 default:
32010                 this.setSize(sz.width,  sz.height);
32011             }
32012         }
32013         this.el.alignTo(this.boundEl, this.alignment);
32014         this.editing = true;
32015         if(Roo.QuickTips){
32016             Roo.QuickTips.disable();
32017         }
32018         this.show();
32019     },
32020
32021     /**
32022      * Sets the height and width of this editor.
32023      * @param {Number} width The new width
32024      * @param {Number} height The new height
32025      */
32026     setSize : function(w, h){
32027         this.field.setSize(w, h);
32028         if(this.el){
32029             this.el.sync();
32030         }
32031     },
32032
32033     /**
32034      * Realigns the editor to the bound field based on the current alignment config value.
32035      */
32036     realign : function(){
32037         this.el.alignTo(this.boundEl, this.alignment);
32038     },
32039
32040     /**
32041      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32042      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32043      */
32044     completeEdit : function(remainVisible){
32045         if(!this.editing){
32046             return;
32047         }
32048         var v = this.getValue();
32049         if(this.revertInvalid !== false && !this.field.isValid()){
32050             v = this.startValue;
32051             this.cancelEdit(true);
32052         }
32053         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32054             this.editing = false;
32055             this.hide();
32056             return;
32057         }
32058         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32059             this.editing = false;
32060             if(this.updateEl && this.boundEl){
32061                 this.boundEl.update(v);
32062             }
32063             if(remainVisible !== true){
32064                 this.hide();
32065             }
32066             this.fireEvent("complete", this, v, this.startValue);
32067         }
32068     },
32069
32070     // private
32071     onShow : function(){
32072         this.el.show();
32073         if(this.hideEl !== false){
32074             this.boundEl.hide();
32075         }
32076         this.field.show();
32077         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32078             this.fixIEFocus = true;
32079             this.deferredFocus.defer(50, this);
32080         }else{
32081             this.field.focus();
32082         }
32083         this.fireEvent("startedit", this.boundEl, this.startValue);
32084     },
32085
32086     deferredFocus : function(){
32087         if(this.editing){
32088             this.field.focus();
32089         }
32090     },
32091
32092     /**
32093      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32094      * reverted to the original starting value.
32095      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32096      * cancel (defaults to false)
32097      */
32098     cancelEdit : function(remainVisible){
32099         if(this.editing){
32100             this.setValue(this.startValue);
32101             if(remainVisible !== true){
32102                 this.hide();
32103             }
32104         }
32105     },
32106
32107     // private
32108     onBlur : function(){
32109         if(this.allowBlur !== true && this.editing){
32110             this.completeEdit();
32111         }
32112     },
32113
32114     // private
32115     onHide : function(){
32116         if(this.editing){
32117             this.completeEdit();
32118             return;
32119         }
32120         this.field.blur();
32121         if(this.field.collapse){
32122             this.field.collapse();
32123         }
32124         this.el.hide();
32125         if(this.hideEl !== false){
32126             this.boundEl.show();
32127         }
32128         if(Roo.QuickTips){
32129             Roo.QuickTips.enable();
32130         }
32131     },
32132
32133     /**
32134      * Sets the data value of the editor
32135      * @param {Mixed} value Any valid value supported by the underlying field
32136      */
32137     setValue : function(v){
32138         this.field.setValue(v);
32139     },
32140
32141     /**
32142      * Gets the data value of the editor
32143      * @return {Mixed} The data value
32144      */
32145     getValue : function(){
32146         return this.field.getValue();
32147     }
32148 });/*
32149  * Based on:
32150  * Ext JS Library 1.1.1
32151  * Copyright(c) 2006-2007, Ext JS, LLC.
32152  *
32153  * Originally Released Under LGPL - original licence link has changed is not relivant.
32154  *
32155  * Fork - LGPL
32156  * <script type="text/javascript">
32157  */
32158  
32159 /**
32160  * @class Roo.BasicDialog
32161  * @extends Roo.util.Observable
32162  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32163  * <pre><code>
32164 var dlg = new Roo.BasicDialog("my-dlg", {
32165     height: 200,
32166     width: 300,
32167     minHeight: 100,
32168     minWidth: 150,
32169     modal: true,
32170     proxyDrag: true,
32171     shadow: true
32172 });
32173 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32174 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32175 dlg.addButton('Cancel', dlg.hide, dlg);
32176 dlg.show();
32177 </code></pre>
32178   <b>A Dialog should always be a direct child of the body element.</b>
32179  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32180  * @cfg {String} title Default text to display in the title bar (defaults to null)
32181  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32182  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32183  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32184  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32185  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32186  * (defaults to null with no animation)
32187  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32188  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32189  * property for valid values (defaults to 'all')
32190  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32191  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32192  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32193  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32194  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32195  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32196  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32197  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32198  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32199  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32200  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32201  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32202  * draggable = true (defaults to false)
32203  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32204  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32205  * shadow (defaults to false)
32206  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32207  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32208  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32209  * @cfg {Array} buttons Array of buttons
32210  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32211  * @constructor
32212  * Create a new BasicDialog.
32213  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32214  * @param {Object} config Configuration options
32215  */
32216 Roo.BasicDialog = function(el, config){
32217     this.el = Roo.get(el);
32218     var dh = Roo.DomHelper;
32219     if(!this.el && config && config.autoCreate){
32220         if(typeof config.autoCreate == "object"){
32221             if(!config.autoCreate.id){
32222                 config.autoCreate.id = el;
32223             }
32224             this.el = dh.append(document.body,
32225                         config.autoCreate, true);
32226         }else{
32227             this.el = dh.append(document.body,
32228                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32229         }
32230     }
32231     el = this.el;
32232     el.setDisplayed(true);
32233     el.hide = this.hideAction;
32234     this.id = el.id;
32235     el.addClass("x-dlg");
32236
32237     Roo.apply(this, config);
32238
32239     this.proxy = el.createProxy("x-dlg-proxy");
32240     this.proxy.hide = this.hideAction;
32241     this.proxy.setOpacity(.5);
32242     this.proxy.hide();
32243
32244     if(config.width){
32245         el.setWidth(config.width);
32246     }
32247     if(config.height){
32248         el.setHeight(config.height);
32249     }
32250     this.size = el.getSize();
32251     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32252         this.xy = [config.x,config.y];
32253     }else{
32254         this.xy = el.getCenterXY(true);
32255     }
32256     /** The header element @type Roo.Element */
32257     this.header = el.child("> .x-dlg-hd");
32258     /** The body element @type Roo.Element */
32259     this.body = el.child("> .x-dlg-bd");
32260     /** The footer element @type Roo.Element */
32261     this.footer = el.child("> .x-dlg-ft");
32262
32263     if(!this.header){
32264         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32265     }
32266     if(!this.body){
32267         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32268     }
32269
32270     this.header.unselectable();
32271     if(this.title){
32272         this.header.update(this.title);
32273     }
32274     // this element allows the dialog to be focused for keyboard event
32275     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32276     this.focusEl.swallowEvent("click", true);
32277
32278     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32279
32280     // wrap the body and footer for special rendering
32281     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32282     if(this.footer){
32283         this.bwrap.dom.appendChild(this.footer.dom);
32284     }
32285
32286     this.bg = this.el.createChild({
32287         tag: "div", cls:"x-dlg-bg",
32288         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32289     });
32290     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32291
32292
32293     if(this.autoScroll !== false && !this.autoTabs){
32294         this.body.setStyle("overflow", "auto");
32295     }
32296
32297     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32298
32299     if(this.closable !== false){
32300         this.el.addClass("x-dlg-closable");
32301         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32302         this.close.on("click", this.closeClick, this);
32303         this.close.addClassOnOver("x-dlg-close-over");
32304     }
32305     if(this.collapsible !== false){
32306         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32307         this.collapseBtn.on("click", this.collapseClick, this);
32308         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32309         this.header.on("dblclick", this.collapseClick, this);
32310     }
32311     if(this.resizable !== false){
32312         this.el.addClass("x-dlg-resizable");
32313         this.resizer = new Roo.Resizable(el, {
32314             minWidth: this.minWidth || 80,
32315             minHeight:this.minHeight || 80,
32316             handles: this.resizeHandles || "all",
32317             pinned: true
32318         });
32319         this.resizer.on("beforeresize", this.beforeResize, this);
32320         this.resizer.on("resize", this.onResize, this);
32321     }
32322     if(this.draggable !== false){
32323         el.addClass("x-dlg-draggable");
32324         if (!this.proxyDrag) {
32325             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32326         }
32327         else {
32328             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32329         }
32330         dd.setHandleElId(this.header.id);
32331         dd.endDrag = this.endMove.createDelegate(this);
32332         dd.startDrag = this.startMove.createDelegate(this);
32333         dd.onDrag = this.onDrag.createDelegate(this);
32334         dd.scroll = false;
32335         this.dd = dd;
32336     }
32337     if(this.modal){
32338         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32339         this.mask.enableDisplayMode("block");
32340         this.mask.hide();
32341         this.el.addClass("x-dlg-modal");
32342     }
32343     if(this.shadow){
32344         this.shadow = new Roo.Shadow({
32345             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32346             offset : this.shadowOffset
32347         });
32348     }else{
32349         this.shadowOffset = 0;
32350     }
32351     if(Roo.useShims && this.shim !== false){
32352         this.shim = this.el.createShim();
32353         this.shim.hide = this.hideAction;
32354         this.shim.hide();
32355     }else{
32356         this.shim = false;
32357     }
32358     if(this.autoTabs){
32359         this.initTabs();
32360     }
32361     if (this.buttons) { 
32362         var bts= this.buttons;
32363         this.buttons = [];
32364         Roo.each(bts, function(b) {
32365             this.addButton(b);
32366         }, this);
32367     }
32368     
32369     
32370     this.addEvents({
32371         /**
32372          * @event keydown
32373          * Fires when a key is pressed
32374          * @param {Roo.BasicDialog} this
32375          * @param {Roo.EventObject} e
32376          */
32377         "keydown" : true,
32378         /**
32379          * @event move
32380          * Fires when this dialog is moved by the user.
32381          * @param {Roo.BasicDialog} this
32382          * @param {Number} x The new page X
32383          * @param {Number} y The new page Y
32384          */
32385         "move" : true,
32386         /**
32387          * @event resize
32388          * Fires when this dialog is resized by the user.
32389          * @param {Roo.BasicDialog} this
32390          * @param {Number} width The new width
32391          * @param {Number} height The new height
32392          */
32393         "resize" : true,
32394         /**
32395          * @event beforehide
32396          * Fires before this dialog is hidden.
32397          * @param {Roo.BasicDialog} this
32398          */
32399         "beforehide" : true,
32400         /**
32401          * @event hide
32402          * Fires when this dialog is hidden.
32403          * @param {Roo.BasicDialog} this
32404          */
32405         "hide" : true,
32406         /**
32407          * @event beforeshow
32408          * Fires before this dialog is shown.
32409          * @param {Roo.BasicDialog} this
32410          */
32411         "beforeshow" : true,
32412         /**
32413          * @event show
32414          * Fires when this dialog is shown.
32415          * @param {Roo.BasicDialog} this
32416          */
32417         "show" : true
32418     });
32419     el.on("keydown", this.onKeyDown, this);
32420     el.on("mousedown", this.toFront, this);
32421     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32422     this.el.hide();
32423     Roo.DialogManager.register(this);
32424     Roo.BasicDialog.superclass.constructor.call(this);
32425 };
32426
32427 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32428     shadowOffset: Roo.isIE ? 6 : 5,
32429     minHeight: 80,
32430     minWidth: 200,
32431     minButtonWidth: 75,
32432     defaultButton: null,
32433     buttonAlign: "right",
32434     tabTag: 'div',
32435     firstShow: true,
32436
32437     /**
32438      * Sets the dialog title text
32439      * @param {String} text The title text to display
32440      * @return {Roo.BasicDialog} this
32441      */
32442     setTitle : function(text){
32443         this.header.update(text);
32444         return this;
32445     },
32446
32447     // private
32448     closeClick : function(){
32449         this.hide();
32450     },
32451
32452     // private
32453     collapseClick : function(){
32454         this[this.collapsed ? "expand" : "collapse"]();
32455     },
32456
32457     /**
32458      * Collapses the dialog to its minimized state (only the title bar is visible).
32459      * Equivalent to the user clicking the collapse dialog button.
32460      */
32461     collapse : function(){
32462         if(!this.collapsed){
32463             this.collapsed = true;
32464             this.el.addClass("x-dlg-collapsed");
32465             this.restoreHeight = this.el.getHeight();
32466             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32467         }
32468     },
32469
32470     /**
32471      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32472      * clicking the expand dialog button.
32473      */
32474     expand : function(){
32475         if(this.collapsed){
32476             this.collapsed = false;
32477             this.el.removeClass("x-dlg-collapsed");
32478             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32479         }
32480     },
32481
32482     /**
32483      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32484      * @return {Roo.TabPanel} The tabs component
32485      */
32486     initTabs : function(){
32487         var tabs = this.getTabs();
32488         while(tabs.getTab(0)){
32489             tabs.removeTab(0);
32490         }
32491         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32492             var dom = el.dom;
32493             tabs.addTab(Roo.id(dom), dom.title);
32494             dom.title = "";
32495         });
32496         tabs.activate(0);
32497         return tabs;
32498     },
32499
32500     // private
32501     beforeResize : function(){
32502         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32503     },
32504
32505     // private
32506     onResize : function(){
32507         this.refreshSize();
32508         this.syncBodyHeight();
32509         this.adjustAssets();
32510         this.focus();
32511         this.fireEvent("resize", this, this.size.width, this.size.height);
32512     },
32513
32514     // private
32515     onKeyDown : function(e){
32516         if(this.isVisible()){
32517             this.fireEvent("keydown", this, e);
32518         }
32519     },
32520
32521     /**
32522      * Resizes the dialog.
32523      * @param {Number} width
32524      * @param {Number} height
32525      * @return {Roo.BasicDialog} this
32526      */
32527     resizeTo : function(width, height){
32528         this.el.setSize(width, height);
32529         this.size = {width: width, height: height};
32530         this.syncBodyHeight();
32531         if(this.fixedcenter){
32532             this.center();
32533         }
32534         if(this.isVisible()){
32535             this.constrainXY();
32536             this.adjustAssets();
32537         }
32538         this.fireEvent("resize", this, width, height);
32539         return this;
32540     },
32541
32542
32543     /**
32544      * Resizes the dialog to fit the specified content size.
32545      * @param {Number} width
32546      * @param {Number} height
32547      * @return {Roo.BasicDialog} this
32548      */
32549     setContentSize : function(w, h){
32550         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32551         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32552         //if(!this.el.isBorderBox()){
32553             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32554             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32555         //}
32556         if(this.tabs){
32557             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32558             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32559         }
32560         this.resizeTo(w, h);
32561         return this;
32562     },
32563
32564     /**
32565      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32566      * executed in response to a particular key being pressed while the dialog is active.
32567      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32568      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32569      * @param {Function} fn The function to call
32570      * @param {Object} scope (optional) The scope of the function
32571      * @return {Roo.BasicDialog} this
32572      */
32573     addKeyListener : function(key, fn, scope){
32574         var keyCode, shift, ctrl, alt;
32575         if(typeof key == "object" && !(key instanceof Array)){
32576             keyCode = key["key"];
32577             shift = key["shift"];
32578             ctrl = key["ctrl"];
32579             alt = key["alt"];
32580         }else{
32581             keyCode = key;
32582         }
32583         var handler = function(dlg, e){
32584             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32585                 var k = e.getKey();
32586                 if(keyCode instanceof Array){
32587                     for(var i = 0, len = keyCode.length; i < len; i++){
32588                         if(keyCode[i] == k){
32589                           fn.call(scope || window, dlg, k, e);
32590                           return;
32591                         }
32592                     }
32593                 }else{
32594                     if(k == keyCode){
32595                         fn.call(scope || window, dlg, k, e);
32596                     }
32597                 }
32598             }
32599         };
32600         this.on("keydown", handler);
32601         return this;
32602     },
32603
32604     /**
32605      * Returns the TabPanel component (creates it if it doesn't exist).
32606      * Note: If you wish to simply check for the existence of tabs without creating them,
32607      * check for a null 'tabs' property.
32608      * @return {Roo.TabPanel} The tabs component
32609      */
32610     getTabs : function(){
32611         if(!this.tabs){
32612             this.el.addClass("x-dlg-auto-tabs");
32613             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32614             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32615         }
32616         return this.tabs;
32617     },
32618
32619     /**
32620      * Adds a button to the footer section of the dialog.
32621      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32622      * object or a valid Roo.DomHelper element config
32623      * @param {Function} handler The function called when the button is clicked
32624      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32625      * @return {Roo.Button} The new button
32626      */
32627     addButton : function(config, handler, scope){
32628         var dh = Roo.DomHelper;
32629         if(!this.footer){
32630             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32631         }
32632         if(!this.btnContainer){
32633             var tb = this.footer.createChild({
32634
32635                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32636                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32637             }, null, true);
32638             this.btnContainer = tb.firstChild.firstChild.firstChild;
32639         }
32640         var bconfig = {
32641             handler: handler,
32642             scope: scope,
32643             minWidth: this.minButtonWidth,
32644             hideParent:true
32645         };
32646         if(typeof config == "string"){
32647             bconfig.text = config;
32648         }else{
32649             if(config.tag){
32650                 bconfig.dhconfig = config;
32651             }else{
32652                 Roo.apply(bconfig, config);
32653             }
32654         }
32655         var fc = false;
32656         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32657             bconfig.position = Math.max(0, bconfig.position);
32658             fc = this.btnContainer.childNodes[bconfig.position];
32659         }
32660          
32661         var btn = new Roo.Button(
32662             fc ? 
32663                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32664                 : this.btnContainer.appendChild(document.createElement("td")),
32665             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32666             bconfig
32667         );
32668         this.syncBodyHeight();
32669         if(!this.buttons){
32670             /**
32671              * Array of all the buttons that have been added to this dialog via addButton
32672              * @type Array
32673              */
32674             this.buttons = [];
32675         }
32676         this.buttons.push(btn);
32677         return btn;
32678     },
32679
32680     /**
32681      * Sets the default button to be focused when the dialog is displayed.
32682      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32683      * @return {Roo.BasicDialog} this
32684      */
32685     setDefaultButton : function(btn){
32686         this.defaultButton = btn;
32687         return this;
32688     },
32689
32690     // private
32691     getHeaderFooterHeight : function(safe){
32692         var height = 0;
32693         if(this.header){
32694            height += this.header.getHeight();
32695         }
32696         if(this.footer){
32697            var fm = this.footer.getMargins();
32698             height += (this.footer.getHeight()+fm.top+fm.bottom);
32699         }
32700         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32701         height += this.centerBg.getPadding("tb");
32702         return height;
32703     },
32704
32705     // private
32706     syncBodyHeight : function()
32707     {
32708         var bd = this.body, // the text
32709             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32710             bw = this.bwrap;
32711         var height = this.size.height - this.getHeaderFooterHeight(false);
32712         bd.setHeight(height-bd.getMargins("tb"));
32713         var hh = this.header.getHeight();
32714         var h = this.size.height-hh;
32715         cb.setHeight(h);
32716         
32717         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32718         bw.setHeight(h-cb.getPadding("tb"));
32719         
32720         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32721         bd.setWidth(bw.getWidth(true));
32722         if(this.tabs){
32723             this.tabs.syncHeight();
32724             if(Roo.isIE){
32725                 this.tabs.el.repaint();
32726             }
32727         }
32728     },
32729
32730     /**
32731      * Restores the previous state of the dialog if Roo.state is configured.
32732      * @return {Roo.BasicDialog} this
32733      */
32734     restoreState : function(){
32735         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32736         if(box && box.width){
32737             this.xy = [box.x, box.y];
32738             this.resizeTo(box.width, box.height);
32739         }
32740         return this;
32741     },
32742
32743     // private
32744     beforeShow : function(){
32745         this.expand();
32746         if(this.fixedcenter){
32747             this.xy = this.el.getCenterXY(true);
32748         }
32749         if(this.modal){
32750             Roo.get(document.body).addClass("x-body-masked");
32751             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32752             this.mask.show();
32753         }
32754         this.constrainXY();
32755     },
32756
32757     // private
32758     animShow : function(){
32759         var b = Roo.get(this.animateTarget).getBox();
32760         this.proxy.setSize(b.width, b.height);
32761         this.proxy.setLocation(b.x, b.y);
32762         this.proxy.show();
32763         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32764                     true, .35, this.showEl.createDelegate(this));
32765     },
32766
32767     /**
32768      * Shows the dialog.
32769      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32770      * @return {Roo.BasicDialog} this
32771      */
32772     show : function(animateTarget){
32773         if (this.fireEvent("beforeshow", this) === false){
32774             return;
32775         }
32776         if(this.syncHeightBeforeShow){
32777             this.syncBodyHeight();
32778         }else if(this.firstShow){
32779             this.firstShow = false;
32780             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32781         }
32782         this.animateTarget = animateTarget || this.animateTarget;
32783         if(!this.el.isVisible()){
32784             this.beforeShow();
32785             if(this.animateTarget && Roo.get(this.animateTarget)){
32786                 this.animShow();
32787             }else{
32788                 this.showEl();
32789             }
32790         }
32791         return this;
32792     },
32793
32794     // private
32795     showEl : function(){
32796         this.proxy.hide();
32797         this.el.setXY(this.xy);
32798         this.el.show();
32799         this.adjustAssets(true);
32800         this.toFront();
32801         this.focus();
32802         // IE peekaboo bug - fix found by Dave Fenwick
32803         if(Roo.isIE){
32804             this.el.repaint();
32805         }
32806         this.fireEvent("show", this);
32807     },
32808
32809     /**
32810      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32811      * dialog itself will receive focus.
32812      */
32813     focus : function(){
32814         if(this.defaultButton){
32815             this.defaultButton.focus();
32816         }else{
32817             this.focusEl.focus();
32818         }
32819     },
32820
32821     // private
32822     constrainXY : function(){
32823         if(this.constraintoviewport !== false){
32824             if(!this.viewSize){
32825                 if(this.container){
32826                     var s = this.container.getSize();
32827                     this.viewSize = [s.width, s.height];
32828                 }else{
32829                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32830                 }
32831             }
32832             var s = Roo.get(this.container||document).getScroll();
32833
32834             var x = this.xy[0], y = this.xy[1];
32835             var w = this.size.width, h = this.size.height;
32836             var vw = this.viewSize[0], vh = this.viewSize[1];
32837             // only move it if it needs it
32838             var moved = false;
32839             // first validate right/bottom
32840             if(x + w > vw+s.left){
32841                 x = vw - w;
32842                 moved = true;
32843             }
32844             if(y + h > vh+s.top){
32845                 y = vh - h;
32846                 moved = true;
32847             }
32848             // then make sure top/left isn't negative
32849             if(x < s.left){
32850                 x = s.left;
32851                 moved = true;
32852             }
32853             if(y < s.top){
32854                 y = s.top;
32855                 moved = true;
32856             }
32857             if(moved){
32858                 // cache xy
32859                 this.xy = [x, y];
32860                 if(this.isVisible()){
32861                     this.el.setLocation(x, y);
32862                     this.adjustAssets();
32863                 }
32864             }
32865         }
32866     },
32867
32868     // private
32869     onDrag : function(){
32870         if(!this.proxyDrag){
32871             this.xy = this.el.getXY();
32872             this.adjustAssets();
32873         }
32874     },
32875
32876     // private
32877     adjustAssets : function(doShow){
32878         var x = this.xy[0], y = this.xy[1];
32879         var w = this.size.width, h = this.size.height;
32880         if(doShow === true){
32881             if(this.shadow){
32882                 this.shadow.show(this.el);
32883             }
32884             if(this.shim){
32885                 this.shim.show();
32886             }
32887         }
32888         if(this.shadow && this.shadow.isVisible()){
32889             this.shadow.show(this.el);
32890         }
32891         if(this.shim && this.shim.isVisible()){
32892             this.shim.setBounds(x, y, w, h);
32893         }
32894     },
32895
32896     // private
32897     adjustViewport : function(w, h){
32898         if(!w || !h){
32899             w = Roo.lib.Dom.getViewWidth();
32900             h = Roo.lib.Dom.getViewHeight();
32901         }
32902         // cache the size
32903         this.viewSize = [w, h];
32904         if(this.modal && this.mask.isVisible()){
32905             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32906             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32907         }
32908         if(this.isVisible()){
32909             this.constrainXY();
32910         }
32911     },
32912
32913     /**
32914      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32915      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32916      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32917      */
32918     destroy : function(removeEl){
32919         if(this.isVisible()){
32920             this.animateTarget = null;
32921             this.hide();
32922         }
32923         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32924         if(this.tabs){
32925             this.tabs.destroy(removeEl);
32926         }
32927         Roo.destroy(
32928              this.shim,
32929              this.proxy,
32930              this.resizer,
32931              this.close,
32932              this.mask
32933         );
32934         if(this.dd){
32935             this.dd.unreg();
32936         }
32937         if(this.buttons){
32938            for(var i = 0, len = this.buttons.length; i < len; i++){
32939                this.buttons[i].destroy();
32940            }
32941         }
32942         this.el.removeAllListeners();
32943         if(removeEl === true){
32944             this.el.update("");
32945             this.el.remove();
32946         }
32947         Roo.DialogManager.unregister(this);
32948     },
32949
32950     // private
32951     startMove : function(){
32952         if(this.proxyDrag){
32953             this.proxy.show();
32954         }
32955         if(this.constraintoviewport !== false){
32956             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32957         }
32958     },
32959
32960     // private
32961     endMove : function(){
32962         if(!this.proxyDrag){
32963             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32964         }else{
32965             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32966             this.proxy.hide();
32967         }
32968         this.refreshSize();
32969         this.adjustAssets();
32970         this.focus();
32971         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32972     },
32973
32974     /**
32975      * Brings this dialog to the front of any other visible dialogs
32976      * @return {Roo.BasicDialog} this
32977      */
32978     toFront : function(){
32979         Roo.DialogManager.bringToFront(this);
32980         return this;
32981     },
32982
32983     /**
32984      * Sends this dialog to the back (under) of any other visible dialogs
32985      * @return {Roo.BasicDialog} this
32986      */
32987     toBack : function(){
32988         Roo.DialogManager.sendToBack(this);
32989         return this;
32990     },
32991
32992     /**
32993      * Centers this dialog in the viewport
32994      * @return {Roo.BasicDialog} this
32995      */
32996     center : function(){
32997         var xy = this.el.getCenterXY(true);
32998         this.moveTo(xy[0], xy[1]);
32999         return this;
33000     },
33001
33002     /**
33003      * Moves the dialog's top-left corner to the specified point
33004      * @param {Number} x
33005      * @param {Number} y
33006      * @return {Roo.BasicDialog} this
33007      */
33008     moveTo : function(x, y){
33009         this.xy = [x,y];
33010         if(this.isVisible()){
33011             this.el.setXY(this.xy);
33012             this.adjustAssets();
33013         }
33014         return this;
33015     },
33016
33017     /**
33018      * Aligns the dialog to the specified element
33019      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33020      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33021      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33022      * @return {Roo.BasicDialog} this
33023      */
33024     alignTo : function(element, position, offsets){
33025         this.xy = this.el.getAlignToXY(element, position, offsets);
33026         if(this.isVisible()){
33027             this.el.setXY(this.xy);
33028             this.adjustAssets();
33029         }
33030         return this;
33031     },
33032
33033     /**
33034      * Anchors an element to another element and realigns it when the window is resized.
33035      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33036      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33037      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33038      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33039      * is a number, it is used as the buffer delay (defaults to 50ms).
33040      * @return {Roo.BasicDialog} this
33041      */
33042     anchorTo : function(el, alignment, offsets, monitorScroll){
33043         var action = function(){
33044             this.alignTo(el, alignment, offsets);
33045         };
33046         Roo.EventManager.onWindowResize(action, this);
33047         var tm = typeof monitorScroll;
33048         if(tm != 'undefined'){
33049             Roo.EventManager.on(window, 'scroll', action, this,
33050                 {buffer: tm == 'number' ? monitorScroll : 50});
33051         }
33052         action.call(this);
33053         return this;
33054     },
33055
33056     /**
33057      * Returns true if the dialog is visible
33058      * @return {Boolean}
33059      */
33060     isVisible : function(){
33061         return this.el.isVisible();
33062     },
33063
33064     // private
33065     animHide : function(callback){
33066         var b = Roo.get(this.animateTarget).getBox();
33067         this.proxy.show();
33068         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33069         this.el.hide();
33070         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33071                     this.hideEl.createDelegate(this, [callback]));
33072     },
33073
33074     /**
33075      * Hides the dialog.
33076      * @param {Function} callback (optional) Function to call when the dialog is hidden
33077      * @return {Roo.BasicDialog} this
33078      */
33079     hide : function(callback){
33080         if (this.fireEvent("beforehide", this) === false){
33081             return;
33082         }
33083         if(this.shadow){
33084             this.shadow.hide();
33085         }
33086         if(this.shim) {
33087           this.shim.hide();
33088         }
33089         // sometimes animateTarget seems to get set.. causing problems...
33090         // this just double checks..
33091         if(this.animateTarget && Roo.get(this.animateTarget)) {
33092            this.animHide(callback);
33093         }else{
33094             this.el.hide();
33095             this.hideEl(callback);
33096         }
33097         return this;
33098     },
33099
33100     // private
33101     hideEl : function(callback){
33102         this.proxy.hide();
33103         if(this.modal){
33104             this.mask.hide();
33105             Roo.get(document.body).removeClass("x-body-masked");
33106         }
33107         this.fireEvent("hide", this);
33108         if(typeof callback == "function"){
33109             callback();
33110         }
33111     },
33112
33113     // private
33114     hideAction : function(){
33115         this.setLeft("-10000px");
33116         this.setTop("-10000px");
33117         this.setStyle("visibility", "hidden");
33118     },
33119
33120     // private
33121     refreshSize : function(){
33122         this.size = this.el.getSize();
33123         this.xy = this.el.getXY();
33124         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33125     },
33126
33127     // private
33128     // z-index is managed by the DialogManager and may be overwritten at any time
33129     setZIndex : function(index){
33130         if(this.modal){
33131             this.mask.setStyle("z-index", index);
33132         }
33133         if(this.shim){
33134             this.shim.setStyle("z-index", ++index);
33135         }
33136         if(this.shadow){
33137             this.shadow.setZIndex(++index);
33138         }
33139         this.el.setStyle("z-index", ++index);
33140         if(this.proxy){
33141             this.proxy.setStyle("z-index", ++index);
33142         }
33143         if(this.resizer){
33144             this.resizer.proxy.setStyle("z-index", ++index);
33145         }
33146
33147         this.lastZIndex = index;
33148     },
33149
33150     /**
33151      * Returns the element for this dialog
33152      * @return {Roo.Element} The underlying dialog Element
33153      */
33154     getEl : function(){
33155         return this.el;
33156     }
33157 });
33158
33159 /**
33160  * @class Roo.DialogManager
33161  * Provides global access to BasicDialogs that have been created and
33162  * support for z-indexing (layering) multiple open dialogs.
33163  */
33164 Roo.DialogManager = function(){
33165     var list = {};
33166     var accessList = [];
33167     var front = null;
33168
33169     // private
33170     var sortDialogs = function(d1, d2){
33171         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33172     };
33173
33174     // private
33175     var orderDialogs = function(){
33176         accessList.sort(sortDialogs);
33177         var seed = Roo.DialogManager.zseed;
33178         for(var i = 0, len = accessList.length; i < len; i++){
33179             var dlg = accessList[i];
33180             if(dlg){
33181                 dlg.setZIndex(seed + (i*10));
33182             }
33183         }
33184     };
33185
33186     return {
33187         /**
33188          * The starting z-index for BasicDialogs (defaults to 9000)
33189          * @type Number The z-index value
33190          */
33191         zseed : 9000,
33192
33193         // private
33194         register : function(dlg){
33195             list[dlg.id] = dlg;
33196             accessList.push(dlg);
33197         },
33198
33199         // private
33200         unregister : function(dlg){
33201             delete list[dlg.id];
33202             var i=0;
33203             var len=0;
33204             if(!accessList.indexOf){
33205                 for(  i = 0, len = accessList.length; i < len; i++){
33206                     if(accessList[i] == dlg){
33207                         accessList.splice(i, 1);
33208                         return;
33209                     }
33210                 }
33211             }else{
33212                  i = accessList.indexOf(dlg);
33213                 if(i != -1){
33214                     accessList.splice(i, 1);
33215                 }
33216             }
33217         },
33218
33219         /**
33220          * Gets a registered dialog by id
33221          * @param {String/Object} id The id of the dialog or a dialog
33222          * @return {Roo.BasicDialog} this
33223          */
33224         get : function(id){
33225             return typeof id == "object" ? id : list[id];
33226         },
33227
33228         /**
33229          * Brings the specified dialog to the front
33230          * @param {String/Object} dlg The id of the dialog or a dialog
33231          * @return {Roo.BasicDialog} this
33232          */
33233         bringToFront : function(dlg){
33234             dlg = this.get(dlg);
33235             if(dlg != front){
33236                 front = dlg;
33237                 dlg._lastAccess = new Date().getTime();
33238                 orderDialogs();
33239             }
33240             return dlg;
33241         },
33242
33243         /**
33244          * Sends the specified dialog to the back
33245          * @param {String/Object} dlg The id of the dialog or a dialog
33246          * @return {Roo.BasicDialog} this
33247          */
33248         sendToBack : function(dlg){
33249             dlg = this.get(dlg);
33250             dlg._lastAccess = -(new Date().getTime());
33251             orderDialogs();
33252             return dlg;
33253         },
33254
33255         /**
33256          * Hides all dialogs
33257          */
33258         hideAll : function(){
33259             for(var id in list){
33260                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33261                     list[id].hide();
33262                 }
33263             }
33264         }
33265     };
33266 }();
33267
33268 /**
33269  * @class Roo.LayoutDialog
33270  * @extends Roo.BasicDialog
33271  * @children Roo.ContentPanel
33272  * @parent builder none
33273  * Dialog which provides adjustments for working with a layout in a Dialog.
33274  * Add your necessary layout config options to the dialog's config.<br>
33275  * Example usage (including a nested layout):
33276  * <pre><code>
33277 if(!dialog){
33278     dialog = new Roo.LayoutDialog("download-dlg", {
33279         modal: true,
33280         width:600,
33281         height:450,
33282         shadow:true,
33283         minWidth:500,
33284         minHeight:350,
33285         autoTabs:true,
33286         proxyDrag:true,
33287         // layout config merges with the dialog config
33288         center:{
33289             tabPosition: "top",
33290             alwaysShowTabs: true
33291         }
33292     });
33293     dialog.addKeyListener(27, dialog.hide, dialog);
33294     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33295     dialog.addButton("Build It!", this.getDownload, this);
33296
33297     // we can even add nested layouts
33298     var innerLayout = new Roo.BorderLayout("dl-inner", {
33299         east: {
33300             initialSize: 200,
33301             autoScroll:true,
33302             split:true
33303         },
33304         center: {
33305             autoScroll:true
33306         }
33307     });
33308     innerLayout.beginUpdate();
33309     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33310     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33311     innerLayout.endUpdate(true);
33312
33313     var layout = dialog.getLayout();
33314     layout.beginUpdate();
33315     layout.add("center", new Roo.ContentPanel("standard-panel",
33316                         {title: "Download the Source", fitToFrame:true}));
33317     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33318                {title: "Build your own roo.js"}));
33319     layout.getRegion("center").showPanel(sp);
33320     layout.endUpdate();
33321 }
33322 </code></pre>
33323     * @constructor
33324     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33325     * @param {Object} config configuration options
33326   */
33327 Roo.LayoutDialog = function(el, cfg){
33328     
33329     var config=  cfg;
33330     if (typeof(cfg) == 'undefined') {
33331         config = Roo.apply({}, el);
33332         // not sure why we use documentElement here.. - it should always be body.
33333         // IE7 borks horribly if we use documentElement.
33334         // webkit also does not like documentElement - it creates a body element...
33335         el = Roo.get( document.body || document.documentElement ).createChild();
33336         //config.autoCreate = true;
33337     }
33338     
33339     
33340     config.autoTabs = false;
33341     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33342     this.body.setStyle({overflow:"hidden", position:"relative"});
33343     this.layout = new Roo.BorderLayout(this.body.dom, config);
33344     this.layout.monitorWindowResize = false;
33345     this.el.addClass("x-dlg-auto-layout");
33346     // fix case when center region overwrites center function
33347     this.center = Roo.BasicDialog.prototype.center;
33348     this.on("show", this.layout.layout, this.layout, true);
33349     if (config.items) {
33350         var xitems = config.items;
33351         delete config.items;
33352         Roo.each(xitems, this.addxtype, this);
33353     }
33354     
33355     
33356 };
33357 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33358     
33359     
33360     /**
33361      * @cfg {Roo.LayoutRegion} east  
33362      */
33363     /**
33364      * @cfg {Roo.LayoutRegion} west
33365      */
33366     /**
33367      * @cfg {Roo.LayoutRegion} south
33368      */
33369     /**
33370      * @cfg {Roo.LayoutRegion} north
33371      */
33372     /**
33373      * @cfg {Roo.LayoutRegion} center
33374      */
33375     /**
33376      * @cfg {Roo.Button} buttons[]  Bottom buttons..
33377      */
33378     
33379     
33380     /**
33381      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33382      * @deprecated
33383      */
33384     endUpdate : function(){
33385         this.layout.endUpdate();
33386     },
33387
33388     /**
33389      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33390      *  @deprecated
33391      */
33392     beginUpdate : function(){
33393         this.layout.beginUpdate();
33394     },
33395
33396     /**
33397      * Get the BorderLayout for this dialog
33398      * @return {Roo.BorderLayout}
33399      */
33400     getLayout : function(){
33401         return this.layout;
33402     },
33403
33404     showEl : function(){
33405         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33406         if(Roo.isIE7){
33407             this.layout.layout();
33408         }
33409     },
33410
33411     // private
33412     // Use the syncHeightBeforeShow config option to control this automatically
33413     syncBodyHeight : function(){
33414         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33415         if(this.layout){this.layout.layout();}
33416     },
33417     
33418       /**
33419      * Add an xtype element (actually adds to the layout.)
33420      * @return {Object} xdata xtype object data.
33421      */
33422     
33423     addxtype : function(c) {
33424         return this.layout.addxtype(c);
33425     }
33426 });/*
33427  * Based on:
33428  * Ext JS Library 1.1.1
33429  * Copyright(c) 2006-2007, Ext JS, LLC.
33430  *
33431  * Originally Released Under LGPL - original licence link has changed is not relivant.
33432  *
33433  * Fork - LGPL
33434  * <script type="text/javascript">
33435  */
33436  
33437 /**
33438  * @class Roo.MessageBox
33439  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33440  * Example usage:
33441  *<pre><code>
33442 // Basic alert:
33443 Roo.Msg.alert('Status', 'Changes saved successfully.');
33444
33445 // Prompt for user data:
33446 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33447     if (btn == 'ok'){
33448         // process text value...
33449     }
33450 });
33451
33452 // Show a dialog using config options:
33453 Roo.Msg.show({
33454    title:'Save Changes?',
33455    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33456    buttons: Roo.Msg.YESNOCANCEL,
33457    fn: processResult,
33458    animEl: 'elId'
33459 });
33460 </code></pre>
33461  * @static
33462  */
33463 Roo.MessageBox = function(){
33464     var dlg, opt, mask, waitTimer;
33465     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33466     var buttons, activeTextEl, bwidth;
33467
33468     // private
33469     var handleButton = function(button){
33470         dlg.hide();
33471         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33472     };
33473
33474     // private
33475     var handleHide = function(){
33476         if(opt && opt.cls){
33477             dlg.el.removeClass(opt.cls);
33478         }
33479         if(waitTimer){
33480             Roo.TaskMgr.stop(waitTimer);
33481             waitTimer = null;
33482         }
33483     };
33484
33485     // private
33486     var updateButtons = function(b){
33487         var width = 0;
33488         if(!b){
33489             buttons["ok"].hide();
33490             buttons["cancel"].hide();
33491             buttons["yes"].hide();
33492             buttons["no"].hide();
33493             dlg.footer.dom.style.display = 'none';
33494             return width;
33495         }
33496         dlg.footer.dom.style.display = '';
33497         for(var k in buttons){
33498             if(typeof buttons[k] != "function"){
33499                 if(b[k]){
33500                     buttons[k].show();
33501                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33502                     width += buttons[k].el.getWidth()+15;
33503                 }else{
33504                     buttons[k].hide();
33505                 }
33506             }
33507         }
33508         return width;
33509     };
33510
33511     // private
33512     var handleEsc = function(d, k, e){
33513         if(opt && opt.closable !== false){
33514             dlg.hide();
33515         }
33516         if(e){
33517             e.stopEvent();
33518         }
33519     };
33520
33521     return {
33522         /**
33523          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33524          * @return {Roo.BasicDialog} The BasicDialog element
33525          */
33526         getDialog : function(){
33527            if(!dlg){
33528                 dlg = new Roo.BasicDialog("x-msg-box", {
33529                     autoCreate : true,
33530                     shadow: true,
33531                     draggable: true,
33532                     resizable:false,
33533                     constraintoviewport:false,
33534                     fixedcenter:true,
33535                     collapsible : false,
33536                     shim:true,
33537                     modal: true,
33538                     width:400, height:100,
33539                     buttonAlign:"center",
33540                     closeClick : function(){
33541                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33542                             handleButton("no");
33543                         }else{
33544                             handleButton("cancel");
33545                         }
33546                     }
33547                 });
33548                 dlg.on("hide", handleHide);
33549                 mask = dlg.mask;
33550                 dlg.addKeyListener(27, handleEsc);
33551                 buttons = {};
33552                 var bt = this.buttonText;
33553                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33554                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33555                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33556                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33557                 bodyEl = dlg.body.createChild({
33558
33559                     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>'
33560                 });
33561                 msgEl = bodyEl.dom.firstChild;
33562                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33563                 textboxEl.enableDisplayMode();
33564                 textboxEl.addKeyListener([10,13], function(){
33565                     if(dlg.isVisible() && opt && opt.buttons){
33566                         if(opt.buttons.ok){
33567                             handleButton("ok");
33568                         }else if(opt.buttons.yes){
33569                             handleButton("yes");
33570                         }
33571                     }
33572                 });
33573                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33574                 textareaEl.enableDisplayMode();
33575                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33576                 progressEl.enableDisplayMode();
33577                 var pf = progressEl.dom.firstChild;
33578                 if (pf) {
33579                     pp = Roo.get(pf.firstChild);
33580                     pp.setHeight(pf.offsetHeight);
33581                 }
33582                 
33583             }
33584             return dlg;
33585         },
33586
33587         /**
33588          * Updates the message box body text
33589          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33590          * the XHTML-compliant non-breaking space character '&amp;#160;')
33591          * @return {Roo.MessageBox} This message box
33592          */
33593         updateText : function(text){
33594             if(!dlg.isVisible() && !opt.width){
33595                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33596             }
33597             msgEl.innerHTML = text || '&#160;';
33598       
33599             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33600             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33601             var w = Math.max(
33602                     Math.min(opt.width || cw , this.maxWidth), 
33603                     Math.max(opt.minWidth || this.minWidth, bwidth)
33604             );
33605             if(opt.prompt){
33606                 activeTextEl.setWidth(w);
33607             }
33608             if(dlg.isVisible()){
33609                 dlg.fixedcenter = false;
33610             }
33611             // to big, make it scroll. = But as usual stupid IE does not support
33612             // !important..
33613             
33614             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33615                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33616                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33617             } else {
33618                 bodyEl.dom.style.height = '';
33619                 bodyEl.dom.style.overflowY = '';
33620             }
33621             if (cw > w) {
33622                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33623             } else {
33624                 bodyEl.dom.style.overflowX = '';
33625             }
33626             
33627             dlg.setContentSize(w, bodyEl.getHeight());
33628             if(dlg.isVisible()){
33629                 dlg.fixedcenter = true;
33630             }
33631             return this;
33632         },
33633
33634         /**
33635          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33636          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33637          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33638          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33639          * @return {Roo.MessageBox} This message box
33640          */
33641         updateProgress : function(value, text){
33642             if(text){
33643                 this.updateText(text);
33644             }
33645             if (pp) { // weird bug on my firefox - for some reason this is not defined
33646                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33647             }
33648             return this;
33649         },        
33650
33651         /**
33652          * Returns true if the message box is currently displayed
33653          * @return {Boolean} True if the message box is visible, else false
33654          */
33655         isVisible : function(){
33656             return dlg && dlg.isVisible();  
33657         },
33658
33659         /**
33660          * Hides the message box if it is displayed
33661          */
33662         hide : function(){
33663             if(this.isVisible()){
33664                 dlg.hide();
33665             }  
33666         },
33667
33668         /**
33669          * Displays a new message box, or reinitializes an existing message box, based on the config options
33670          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33671          * The following config object properties are supported:
33672          * <pre>
33673 Property    Type             Description
33674 ----------  ---------------  ------------------------------------------------------------------------------------
33675 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33676                                    closes (defaults to undefined)
33677 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33678                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33679 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33680                                    progress and wait dialogs will ignore this property and always hide the
33681                                    close button as they can only be closed programmatically.
33682 cls               String           A custom CSS class to apply to the message box element
33683 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33684                                    displayed (defaults to 75)
33685 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33686                                    function will be btn (the name of the button that was clicked, if applicable,
33687                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33688                                    Progress and wait dialogs will ignore this option since they do not respond to
33689                                    user actions and can only be closed programmatically, so any required function
33690                                    should be called by the same code after it closes the dialog.
33691 icon              String           A CSS class that provides a background image to be used as an icon for
33692                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33693 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33694 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33695 modal             Boolean          False to allow user interaction with the page while the message box is
33696                                    displayed (defaults to true)
33697 msg               String           A string that will replace the existing message box body text (defaults
33698                                    to the XHTML-compliant non-breaking space character '&#160;')
33699 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33700 progress          Boolean          True to display a progress bar (defaults to false)
33701 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33702 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33703 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33704 title             String           The title text
33705 value             String           The string value to set into the active textbox element if displayed
33706 wait              Boolean          True to display a progress bar (defaults to false)
33707 width             Number           The width of the dialog in pixels
33708 </pre>
33709          *
33710          * Example usage:
33711          * <pre><code>
33712 Roo.Msg.show({
33713    title: 'Address',
33714    msg: 'Please enter your address:',
33715    width: 300,
33716    buttons: Roo.MessageBox.OKCANCEL,
33717    multiline: true,
33718    fn: saveAddress,
33719    animEl: 'addAddressBtn'
33720 });
33721 </code></pre>
33722          * @param {Object} config Configuration options
33723          * @return {Roo.MessageBox} This message box
33724          */
33725         show : function(options)
33726         {
33727             
33728             // this causes nightmares if you show one dialog after another
33729             // especially on callbacks..
33730              
33731             if(this.isVisible()){
33732                 
33733                 this.hide();
33734                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33735                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33736                 Roo.log("New Dialog Message:" +  options.msg )
33737                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33738                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33739                 
33740             }
33741             var d = this.getDialog();
33742             opt = options;
33743             d.setTitle(opt.title || "&#160;");
33744             d.close.setDisplayed(opt.closable !== false);
33745             activeTextEl = textboxEl;
33746             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33747             if(opt.prompt){
33748                 if(opt.multiline){
33749                     textboxEl.hide();
33750                     textareaEl.show();
33751                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33752                         opt.multiline : this.defaultTextHeight);
33753                     activeTextEl = textareaEl;
33754                 }else{
33755                     textboxEl.show();
33756                     textareaEl.hide();
33757                 }
33758             }else{
33759                 textboxEl.hide();
33760                 textareaEl.hide();
33761             }
33762             progressEl.setDisplayed(opt.progress === true);
33763             this.updateProgress(0);
33764             activeTextEl.dom.value = opt.value || "";
33765             if(opt.prompt){
33766                 dlg.setDefaultButton(activeTextEl);
33767             }else{
33768                 var bs = opt.buttons;
33769                 var db = null;
33770                 if(bs && bs.ok){
33771                     db = buttons["ok"];
33772                 }else if(bs && bs.yes){
33773                     db = buttons["yes"];
33774                 }
33775                 dlg.setDefaultButton(db);
33776             }
33777             bwidth = updateButtons(opt.buttons);
33778             this.updateText(opt.msg);
33779             if(opt.cls){
33780                 d.el.addClass(opt.cls);
33781             }
33782             d.proxyDrag = opt.proxyDrag === true;
33783             d.modal = opt.modal !== false;
33784             d.mask = opt.modal !== false ? mask : false;
33785             if(!d.isVisible()){
33786                 // force it to the end of the z-index stack so it gets a cursor in FF
33787                 document.body.appendChild(dlg.el.dom);
33788                 d.animateTarget = null;
33789                 d.show(options.animEl);
33790             }
33791             return this;
33792         },
33793
33794         /**
33795          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33796          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33797          * and closing the message box when the process is complete.
33798          * @param {String} title The title bar text
33799          * @param {String} msg The message box body text
33800          * @return {Roo.MessageBox} This message box
33801          */
33802         progress : function(title, msg){
33803             this.show({
33804                 title : title,
33805                 msg : msg,
33806                 buttons: false,
33807                 progress:true,
33808                 closable:false,
33809                 minWidth: this.minProgressWidth,
33810                 modal : true
33811             });
33812             return this;
33813         },
33814
33815         /**
33816          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33817          * If a callback function is passed it will be called after the user clicks the button, and the
33818          * id of the button that was clicked will be passed as the only parameter to the callback
33819          * (could also be the top-right close button).
33820          * @param {String} title The title bar text
33821          * @param {String} msg The message box body text
33822          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33823          * @param {Object} scope (optional) The scope of the callback function
33824          * @return {Roo.MessageBox} This message box
33825          */
33826         alert : function(title, msg, fn, scope){
33827             this.show({
33828                 title : title,
33829                 msg : msg,
33830                 buttons: this.OK,
33831                 fn: fn,
33832                 scope : scope,
33833                 modal : true
33834             });
33835             return this;
33836         },
33837
33838         /**
33839          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33840          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33841          * You are responsible for closing the message box when the process is complete.
33842          * @param {String} msg The message box body text
33843          * @param {String} title (optional) The title bar text
33844          * @return {Roo.MessageBox} This message box
33845          */
33846         wait : function(msg, title){
33847             this.show({
33848                 title : title,
33849                 msg : msg,
33850                 buttons: false,
33851                 closable:false,
33852                 progress:true,
33853                 modal:true,
33854                 width:300,
33855                 wait:true
33856             });
33857             waitTimer = Roo.TaskMgr.start({
33858                 run: function(i){
33859                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33860                 },
33861                 interval: 1000
33862             });
33863             return this;
33864         },
33865
33866         /**
33867          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33868          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33869          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33870          * @param {String} title The title bar text
33871          * @param {String} msg The message box body text
33872          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33873          * @param {Object} scope (optional) The scope of the callback function
33874          * @return {Roo.MessageBox} This message box
33875          */
33876         confirm : function(title, msg, fn, scope){
33877             this.show({
33878                 title : title,
33879                 msg : msg,
33880                 buttons: this.YESNO,
33881                 fn: fn,
33882                 scope : scope,
33883                 modal : true
33884             });
33885             return this;
33886         },
33887
33888         /**
33889          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33890          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33891          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33892          * (could also be the top-right close button) and the text that was entered will be passed as the two
33893          * parameters to the callback.
33894          * @param {String} title The title bar text
33895          * @param {String} msg The message box body text
33896          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33897          * @param {Object} scope (optional) The scope of the callback function
33898          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33899          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33900          * @return {Roo.MessageBox} This message box
33901          */
33902         prompt : function(title, msg, fn, scope, multiline){
33903             this.show({
33904                 title : title,
33905                 msg : msg,
33906                 buttons: this.OKCANCEL,
33907                 fn: fn,
33908                 minWidth:250,
33909                 scope : scope,
33910                 prompt:true,
33911                 multiline: multiline,
33912                 modal : true
33913             });
33914             return this;
33915         },
33916
33917         /**
33918          * Button config that displays a single OK button
33919          * @type Object
33920          */
33921         OK : {ok:true},
33922         /**
33923          * Button config that displays Yes and No buttons
33924          * @type Object
33925          */
33926         YESNO : {yes:true, no:true},
33927         /**
33928          * Button config that displays OK and Cancel buttons
33929          * @type Object
33930          */
33931         OKCANCEL : {ok:true, cancel:true},
33932         /**
33933          * Button config that displays Yes, No and Cancel buttons
33934          * @type Object
33935          */
33936         YESNOCANCEL : {yes:true, no:true, cancel:true},
33937
33938         /**
33939          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33940          * @type Number
33941          */
33942         defaultTextHeight : 75,
33943         /**
33944          * The maximum width in pixels of the message box (defaults to 600)
33945          * @type Number
33946          */
33947         maxWidth : 600,
33948         /**
33949          * The minimum width in pixels of the message box (defaults to 100)
33950          * @type Number
33951          */
33952         minWidth : 100,
33953         /**
33954          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33955          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33956          * @type Number
33957          */
33958         minProgressWidth : 250,
33959         /**
33960          * An object containing the default button text strings that can be overriden for localized language support.
33961          * Supported properties are: ok, cancel, yes and no.
33962          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33963          * @type Object
33964          */
33965         buttonText : {
33966             ok : "OK",
33967             cancel : "Cancel",
33968             yes : "Yes",
33969             no : "No"
33970         }
33971     };
33972 }();
33973
33974 /**
33975  * Shorthand for {@link Roo.MessageBox}
33976  */
33977 Roo.Msg = Roo.MessageBox;/*
33978  * Based on:
33979  * Ext JS Library 1.1.1
33980  * Copyright(c) 2006-2007, Ext JS, LLC.
33981  *
33982  * Originally Released Under LGPL - original licence link has changed is not relivant.
33983  *
33984  * Fork - LGPL
33985  * <script type="text/javascript">
33986  */
33987 /**
33988  * @class Roo.QuickTips
33989  * Provides attractive and customizable tooltips for any element.
33990  * @static
33991  */
33992 Roo.QuickTips = function(){
33993     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33994     var ce, bd, xy, dd;
33995     var visible = false, disabled = true, inited = false;
33996     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33997     
33998     var onOver = function(e){
33999         if(disabled){
34000             return;
34001         }
34002         var t = e.getTarget();
34003         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34004             return;
34005         }
34006         if(ce && t == ce.el){
34007             clearTimeout(hideProc);
34008             return;
34009         }
34010         if(t && tagEls[t.id]){
34011             tagEls[t.id].el = t;
34012             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34013             return;
34014         }
34015         var ttp, et = Roo.fly(t);
34016         var ns = cfg.namespace;
34017         if(tm.interceptTitles && t.title){
34018             ttp = t.title;
34019             t.qtip = ttp;
34020             t.removeAttribute("title");
34021             e.preventDefault();
34022         }else{
34023             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34024         }
34025         if(ttp){
34026             showProc = show.defer(tm.showDelay, tm, [{
34027                 el: t, 
34028                 text: ttp.replace(/\\n/g,'<br/>'),
34029                 width: et.getAttributeNS(ns, cfg.width),
34030                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34031                 title: et.getAttributeNS(ns, cfg.title),
34032                     cls: et.getAttributeNS(ns, cfg.cls)
34033             }]);
34034         }
34035     };
34036     
34037     var onOut = function(e){
34038         clearTimeout(showProc);
34039         var t = e.getTarget();
34040         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34041             hideProc = setTimeout(hide, tm.hideDelay);
34042         }
34043     };
34044     
34045     var onMove = function(e){
34046         if(disabled){
34047             return;
34048         }
34049         xy = e.getXY();
34050         xy[1] += 18;
34051         if(tm.trackMouse && ce){
34052             el.setXY(xy);
34053         }
34054     };
34055     
34056     var onDown = function(e){
34057         clearTimeout(showProc);
34058         clearTimeout(hideProc);
34059         if(!e.within(el)){
34060             if(tm.hideOnClick){
34061                 hide();
34062                 tm.disable();
34063                 tm.enable.defer(100, tm);
34064             }
34065         }
34066     };
34067     
34068     var getPad = function(){
34069         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34070     };
34071
34072     var show = function(o){
34073         if(disabled){
34074             return;
34075         }
34076         clearTimeout(dismissProc);
34077         ce = o;
34078         if(removeCls){ // in case manually hidden
34079             el.removeClass(removeCls);
34080             removeCls = null;
34081         }
34082         if(ce.cls){
34083             el.addClass(ce.cls);
34084             removeCls = ce.cls;
34085         }
34086         if(ce.title){
34087             tipTitle.update(ce.title);
34088             tipTitle.show();
34089         }else{
34090             tipTitle.update('');
34091             tipTitle.hide();
34092         }
34093         el.dom.style.width  = tm.maxWidth+'px';
34094         //tipBody.dom.style.width = '';
34095         tipBodyText.update(o.text);
34096         var p = getPad(), w = ce.width;
34097         if(!w){
34098             var td = tipBodyText.dom;
34099             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34100             if(aw > tm.maxWidth){
34101                 w = tm.maxWidth;
34102             }else if(aw < tm.minWidth){
34103                 w = tm.minWidth;
34104             }else{
34105                 w = aw;
34106             }
34107         }
34108         //tipBody.setWidth(w);
34109         el.setWidth(parseInt(w, 10) + p);
34110         if(ce.autoHide === false){
34111             close.setDisplayed(true);
34112             if(dd){
34113                 dd.unlock();
34114             }
34115         }else{
34116             close.setDisplayed(false);
34117             if(dd){
34118                 dd.lock();
34119             }
34120         }
34121         if(xy){
34122             el.avoidY = xy[1]-18;
34123             el.setXY(xy);
34124         }
34125         if(tm.animate){
34126             el.setOpacity(.1);
34127             el.setStyle("visibility", "visible");
34128             el.fadeIn({callback: afterShow});
34129         }else{
34130             afterShow();
34131         }
34132     };
34133     
34134     var afterShow = function(){
34135         if(ce){
34136             el.show();
34137             esc.enable();
34138             if(tm.autoDismiss && ce.autoHide !== false){
34139                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34140             }
34141         }
34142     };
34143     
34144     var hide = function(noanim){
34145         clearTimeout(dismissProc);
34146         clearTimeout(hideProc);
34147         ce = null;
34148         if(el.isVisible()){
34149             esc.disable();
34150             if(noanim !== true && tm.animate){
34151                 el.fadeOut({callback: afterHide});
34152             }else{
34153                 afterHide();
34154             } 
34155         }
34156     };
34157     
34158     var afterHide = function(){
34159         el.hide();
34160         if(removeCls){
34161             el.removeClass(removeCls);
34162             removeCls = null;
34163         }
34164     };
34165     
34166     return {
34167         /**
34168         * @cfg {Number} minWidth
34169         * The minimum width of the quick tip (defaults to 40)
34170         */
34171        minWidth : 40,
34172         /**
34173         * @cfg {Number} maxWidth
34174         * The maximum width of the quick tip (defaults to 300)
34175         */
34176        maxWidth : 300,
34177         /**
34178         * @cfg {Boolean} interceptTitles
34179         * True to automatically use the element's DOM title value if available (defaults to false)
34180         */
34181        interceptTitles : false,
34182         /**
34183         * @cfg {Boolean} trackMouse
34184         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34185         */
34186        trackMouse : false,
34187         /**
34188         * @cfg {Boolean} hideOnClick
34189         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34190         */
34191        hideOnClick : true,
34192         /**
34193         * @cfg {Number} showDelay
34194         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34195         */
34196        showDelay : 500,
34197         /**
34198         * @cfg {Number} hideDelay
34199         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34200         */
34201        hideDelay : 200,
34202         /**
34203         * @cfg {Boolean} autoHide
34204         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34205         * Used in conjunction with hideDelay.
34206         */
34207        autoHide : true,
34208         /**
34209         * @cfg {Boolean}
34210         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34211         * (defaults to true).  Used in conjunction with autoDismissDelay.
34212         */
34213        autoDismiss : true,
34214         /**
34215         * @cfg {Number}
34216         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34217         */
34218        autoDismissDelay : 5000,
34219        /**
34220         * @cfg {Boolean} animate
34221         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34222         */
34223        animate : false,
34224
34225        /**
34226         * @cfg {String} title
34227         * Title text to display (defaults to '').  This can be any valid HTML markup.
34228         */
34229         title: '',
34230        /**
34231         * @cfg {String} text
34232         * Body text to display (defaults to '').  This can be any valid HTML markup.
34233         */
34234         text : '',
34235        /**
34236         * @cfg {String} cls
34237         * A CSS class to apply to the base quick tip element (defaults to '').
34238         */
34239         cls : '',
34240        /**
34241         * @cfg {Number} width
34242         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34243         * minWidth or maxWidth.
34244         */
34245         width : null,
34246
34247     /**
34248      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34249      * or display QuickTips in a page.
34250      */
34251        init : function(){
34252           tm = Roo.QuickTips;
34253           cfg = tm.tagConfig;
34254           if(!inited){
34255               if(!Roo.isReady){ // allow calling of init() before onReady
34256                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34257                   return;
34258               }
34259               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34260               el.fxDefaults = {stopFx: true};
34261               // maximum custom styling
34262               //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>');
34263               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>');              
34264               tipTitle = el.child('h3');
34265               tipTitle.enableDisplayMode("block");
34266               tipBody = el.child('div.x-tip-bd');
34267               tipBodyText = el.child('div.x-tip-bd-inner');
34268               //bdLeft = el.child('div.x-tip-bd-left');
34269               //bdRight = el.child('div.x-tip-bd-right');
34270               close = el.child('div.x-tip-close');
34271               close.enableDisplayMode("block");
34272               close.on("click", hide);
34273               var d = Roo.get(document);
34274               d.on("mousedown", onDown);
34275               d.on("mouseover", onOver);
34276               d.on("mouseout", onOut);
34277               d.on("mousemove", onMove);
34278               esc = d.addKeyListener(27, hide);
34279               esc.disable();
34280               if(Roo.dd.DD){
34281                   dd = el.initDD("default", null, {
34282                       onDrag : function(){
34283                           el.sync();  
34284                       }
34285                   });
34286                   dd.setHandleElId(tipTitle.id);
34287                   dd.lock();
34288               }
34289               inited = true;
34290           }
34291           this.enable(); 
34292        },
34293
34294     /**
34295      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34296      * are supported:
34297      * <pre>
34298 Property    Type                   Description
34299 ----------  ---------------------  ------------------------------------------------------------------------
34300 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34301      * </ul>
34302      * @param {Object} config The config object
34303      */
34304        register : function(config){
34305            var cs = config instanceof Array ? config : arguments;
34306            for(var i = 0, len = cs.length; i < len; i++) {
34307                var c = cs[i];
34308                var target = c.target;
34309                if(target){
34310                    if(target instanceof Array){
34311                        for(var j = 0, jlen = target.length; j < jlen; j++){
34312                            tagEls[target[j]] = c;
34313                        }
34314                    }else{
34315                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34316                    }
34317                }
34318            }
34319        },
34320
34321     /**
34322      * Removes this quick tip from its element and destroys it.
34323      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34324      */
34325        unregister : function(el){
34326            delete tagEls[Roo.id(el)];
34327        },
34328
34329     /**
34330      * Enable this quick tip.
34331      */
34332        enable : function(){
34333            if(inited && disabled){
34334                locks.pop();
34335                if(locks.length < 1){
34336                    disabled = false;
34337                }
34338            }
34339        },
34340
34341     /**
34342      * Disable this quick tip.
34343      */
34344        disable : function(){
34345           disabled = true;
34346           clearTimeout(showProc);
34347           clearTimeout(hideProc);
34348           clearTimeout(dismissProc);
34349           if(ce){
34350               hide(true);
34351           }
34352           locks.push(1);
34353        },
34354
34355     /**
34356      * Returns true if the quick tip is enabled, else false.
34357      */
34358        isEnabled : function(){
34359             return !disabled;
34360        },
34361
34362         // private
34363        tagConfig : {
34364            namespace : "roo", // was ext?? this may break..
34365            alt_namespace : "ext",
34366            attribute : "qtip",
34367            width : "width",
34368            target : "target",
34369            title : "qtitle",
34370            hide : "hide",
34371            cls : "qclass"
34372        }
34373    };
34374 }();
34375
34376 // backwards compat
34377 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34378  * Based on:
34379  * Ext JS Library 1.1.1
34380  * Copyright(c) 2006-2007, Ext JS, LLC.
34381  *
34382  * Originally Released Under LGPL - original licence link has changed is not relivant.
34383  *
34384  * Fork - LGPL
34385  * <script type="text/javascript">
34386  */
34387  
34388
34389 /**
34390  * @class Roo.tree.TreePanel
34391  * @extends Roo.data.Tree
34392  * @cfg {Roo.tree.TreeNode} root The root node
34393  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34394  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34395  * @cfg {Boolean} enableDD true to enable drag and drop
34396  * @cfg {Boolean} enableDrag true to enable just drag
34397  * @cfg {Boolean} enableDrop true to enable just drop
34398  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34399  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34400  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34401  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34402  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34403  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34404  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34405  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34406  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34407  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34408  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34409  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
34410  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
34411  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34412  * @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>
34413  * @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>
34414  * 
34415  * @constructor
34416  * @param {String/HTMLElement/Element} el The container element
34417  * @param {Object} config
34418  */
34419 Roo.tree.TreePanel = function(el, config){
34420     var root = false;
34421     var loader = false;
34422     if (config.root) {
34423         root = config.root;
34424         delete config.root;
34425     }
34426     if (config.loader) {
34427         loader = config.loader;
34428         delete config.loader;
34429     }
34430     
34431     Roo.apply(this, config);
34432     Roo.tree.TreePanel.superclass.constructor.call(this);
34433     this.el = Roo.get(el);
34434     this.el.addClass('x-tree');
34435     //console.log(root);
34436     if (root) {
34437         this.setRootNode( Roo.factory(root, Roo.tree));
34438     }
34439     if (loader) {
34440         this.loader = Roo.factory(loader, Roo.tree);
34441     }
34442    /**
34443     * Read-only. The id of the container element becomes this TreePanel's id.
34444     */
34445     this.id = this.el.id;
34446     this.addEvents({
34447         /**
34448         * @event beforeload
34449         * Fires before a node is loaded, return false to cancel
34450         * @param {Node} node The node being loaded
34451         */
34452         "beforeload" : true,
34453         /**
34454         * @event load
34455         * Fires when a node is loaded
34456         * @param {Node} node The node that was loaded
34457         */
34458         "load" : true,
34459         /**
34460         * @event textchange
34461         * Fires when the text for a node is changed
34462         * @param {Node} node The node
34463         * @param {String} text The new text
34464         * @param {String} oldText The old text
34465         */
34466         "textchange" : true,
34467         /**
34468         * @event beforeexpand
34469         * Fires before a node is expanded, return false to cancel.
34470         * @param {Node} node The node
34471         * @param {Boolean} deep
34472         * @param {Boolean} anim
34473         */
34474         "beforeexpand" : true,
34475         /**
34476         * @event beforecollapse
34477         * Fires before a node is collapsed, return false to cancel.
34478         * @param {Node} node The node
34479         * @param {Boolean} deep
34480         * @param {Boolean} anim
34481         */
34482         "beforecollapse" : true,
34483         /**
34484         * @event expand
34485         * Fires when a node is expanded
34486         * @param {Node} node The node
34487         */
34488         "expand" : true,
34489         /**
34490         * @event disabledchange
34491         * Fires when the disabled status of a node changes
34492         * @param {Node} node The node
34493         * @param {Boolean} disabled
34494         */
34495         "disabledchange" : true,
34496         /**
34497         * @event collapse
34498         * Fires when a node is collapsed
34499         * @param {Node} node The node
34500         */
34501         "collapse" : true,
34502         /**
34503         * @event beforeclick
34504         * Fires before click processing on a node. Return false to cancel the default action.
34505         * @param {Node} node The node
34506         * @param {Roo.EventObject} e The event object
34507         */
34508         "beforeclick":true,
34509         /**
34510         * @event checkchange
34511         * Fires when a node with a checkbox's checked property changes
34512         * @param {Node} this This node
34513         * @param {Boolean} checked
34514         */
34515         "checkchange":true,
34516         /**
34517         * @event click
34518         * Fires when a node is clicked
34519         * @param {Node} node The node
34520         * @param {Roo.EventObject} e The event object
34521         */
34522         "click":true,
34523         /**
34524         * @event dblclick
34525         * Fires when a node is double clicked
34526         * @param {Node} node The node
34527         * @param {Roo.EventObject} e The event object
34528         */
34529         "dblclick":true,
34530         /**
34531         * @event contextmenu
34532         * Fires when a node is right clicked
34533         * @param {Node} node The node
34534         * @param {Roo.EventObject} e The event object
34535         */
34536         "contextmenu":true,
34537         /**
34538         * @event beforechildrenrendered
34539         * Fires right before the child nodes for a node are rendered
34540         * @param {Node} node The node
34541         */
34542         "beforechildrenrendered":true,
34543         /**
34544         * @event startdrag
34545         * Fires when a node starts being dragged
34546         * @param {Roo.tree.TreePanel} this
34547         * @param {Roo.tree.TreeNode} node
34548         * @param {event} e The raw browser event
34549         */ 
34550        "startdrag" : true,
34551        /**
34552         * @event enddrag
34553         * Fires when a drag operation is complete
34554         * @param {Roo.tree.TreePanel} this
34555         * @param {Roo.tree.TreeNode} node
34556         * @param {event} e The raw browser event
34557         */
34558        "enddrag" : true,
34559        /**
34560         * @event dragdrop
34561         * Fires when a dragged node is dropped on a valid DD target
34562         * @param {Roo.tree.TreePanel} this
34563         * @param {Roo.tree.TreeNode} node
34564         * @param {DD} dd The dd it was dropped on
34565         * @param {event} e The raw browser event
34566         */
34567        "dragdrop" : true,
34568        /**
34569         * @event beforenodedrop
34570         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34571         * passed to handlers has the following properties:<br />
34572         * <ul style="padding:5px;padding-left:16px;">
34573         * <li>tree - The TreePanel</li>
34574         * <li>target - The node being targeted for the drop</li>
34575         * <li>data - The drag data from the drag source</li>
34576         * <li>point - The point of the drop - append, above or below</li>
34577         * <li>source - The drag source</li>
34578         * <li>rawEvent - Raw mouse event</li>
34579         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34580         * to be inserted by setting them on this object.</li>
34581         * <li>cancel - Set this to true to cancel the drop.</li>
34582         * </ul>
34583         * @param {Object} dropEvent
34584         */
34585        "beforenodedrop" : true,
34586        /**
34587         * @event nodedrop
34588         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34589         * passed to handlers has the following properties:<br />
34590         * <ul style="padding:5px;padding-left:16px;">
34591         * <li>tree - The TreePanel</li>
34592         * <li>target - The node being targeted for the drop</li>
34593         * <li>data - The drag data from the drag source</li>
34594         * <li>point - The point of the drop - append, above or below</li>
34595         * <li>source - The drag source</li>
34596         * <li>rawEvent - Raw mouse event</li>
34597         * <li>dropNode - Dropped node(s).</li>
34598         * </ul>
34599         * @param {Object} dropEvent
34600         */
34601        "nodedrop" : true,
34602         /**
34603         * @event nodedragover
34604         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34605         * passed to handlers has the following properties:<br />
34606         * <ul style="padding:5px;padding-left:16px;">
34607         * <li>tree - The TreePanel</li>
34608         * <li>target - The node being targeted for the drop</li>
34609         * <li>data - The drag data from the drag source</li>
34610         * <li>point - The point of the drop - append, above or below</li>
34611         * <li>source - The drag source</li>
34612         * <li>rawEvent - Raw mouse event</li>
34613         * <li>dropNode - Drop node(s) provided by the source.</li>
34614         * <li>cancel - Set this to true to signal drop not allowed.</li>
34615         * </ul>
34616         * @param {Object} dragOverEvent
34617         */
34618        "nodedragover" : true,
34619        /**
34620         * @event appendnode
34621         * Fires when append node to the tree
34622         * @param {Roo.tree.TreePanel} this
34623         * @param {Roo.tree.TreeNode} node
34624         * @param {Number} index The index of the newly appended node
34625         */
34626        "appendnode" : true
34627         
34628     });
34629     if(this.singleExpand){
34630        this.on("beforeexpand", this.restrictExpand, this);
34631     }
34632     if (this.editor) {
34633         this.editor.tree = this;
34634         this.editor = Roo.factory(this.editor, Roo.tree);
34635     }
34636     
34637     if (this.selModel) {
34638         this.selModel = Roo.factory(this.selModel, Roo.tree);
34639     }
34640    
34641 };
34642 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34643     rootVisible : true,
34644     animate: Roo.enableFx,
34645     lines : true,
34646     enableDD : false,
34647     hlDrop : Roo.enableFx,
34648   
34649     renderer: false,
34650     
34651     rendererTip: false,
34652     // private
34653     restrictExpand : function(node){
34654         var p = node.parentNode;
34655         if(p){
34656             if(p.expandedChild && p.expandedChild.parentNode == p){
34657                 p.expandedChild.collapse();
34658             }
34659             p.expandedChild = node;
34660         }
34661     },
34662
34663     // private override
34664     setRootNode : function(node){
34665         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34666         if(!this.rootVisible){
34667             node.ui = new Roo.tree.RootTreeNodeUI(node);
34668         }
34669         return node;
34670     },
34671
34672     /**
34673      * Returns the container element for this TreePanel
34674      */
34675     getEl : function(){
34676         return this.el;
34677     },
34678
34679     /**
34680      * Returns the default TreeLoader for this TreePanel
34681      */
34682     getLoader : function(){
34683         return this.loader;
34684     },
34685
34686     /**
34687      * Expand all nodes
34688      */
34689     expandAll : function(){
34690         this.root.expand(true);
34691     },
34692
34693     /**
34694      * Collapse all nodes
34695      */
34696     collapseAll : function(){
34697         this.root.collapse(true);
34698     },
34699
34700     /**
34701      * Returns the selection model used by this TreePanel
34702      */
34703     getSelectionModel : function(){
34704         if(!this.selModel){
34705             this.selModel = new Roo.tree.DefaultSelectionModel();
34706         }
34707         return this.selModel;
34708     },
34709
34710     /**
34711      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34712      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34713      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34714      * @return {Array}
34715      */
34716     getChecked : function(a, startNode){
34717         startNode = startNode || this.root;
34718         var r = [];
34719         var f = function(){
34720             if(this.attributes.checked){
34721                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34722             }
34723         }
34724         startNode.cascade(f);
34725         return r;
34726     },
34727
34728     /**
34729      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34730      * @param {String} path
34731      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34732      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34733      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34734      */
34735     expandPath : function(path, attr, callback){
34736         attr = attr || "id";
34737         var keys = path.split(this.pathSeparator);
34738         var curNode = this.root;
34739         if(curNode.attributes[attr] != keys[1]){ // invalid root
34740             if(callback){
34741                 callback(false, null);
34742             }
34743             return;
34744         }
34745         var index = 1;
34746         var f = function(){
34747             if(++index == keys.length){
34748                 if(callback){
34749                     callback(true, curNode);
34750                 }
34751                 return;
34752             }
34753             var c = curNode.findChild(attr, keys[index]);
34754             if(!c){
34755                 if(callback){
34756                     callback(false, curNode);
34757                 }
34758                 return;
34759             }
34760             curNode = c;
34761             c.expand(false, false, f);
34762         };
34763         curNode.expand(false, false, f);
34764     },
34765
34766     /**
34767      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34768      * @param {String} path
34769      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34770      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34771      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34772      */
34773     selectPath : function(path, attr, callback){
34774         attr = attr || "id";
34775         var keys = path.split(this.pathSeparator);
34776         var v = keys.pop();
34777         if(keys.length > 0){
34778             var f = function(success, node){
34779                 if(success && node){
34780                     var n = node.findChild(attr, v);
34781                     if(n){
34782                         n.select();
34783                         if(callback){
34784                             callback(true, n);
34785                         }
34786                     }else if(callback){
34787                         callback(false, n);
34788                     }
34789                 }else{
34790                     if(callback){
34791                         callback(false, n);
34792                     }
34793                 }
34794             };
34795             this.expandPath(keys.join(this.pathSeparator), attr, f);
34796         }else{
34797             this.root.select();
34798             if(callback){
34799                 callback(true, this.root);
34800             }
34801         }
34802     },
34803
34804     getTreeEl : function(){
34805         return this.el;
34806     },
34807
34808     /**
34809      * Trigger rendering of this TreePanel
34810      */
34811     render : function(){
34812         if (this.innerCt) {
34813             return this; // stop it rendering more than once!!
34814         }
34815         
34816         this.innerCt = this.el.createChild({tag:"ul",
34817                cls:"x-tree-root-ct " +
34818                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34819
34820         if(this.containerScroll){
34821             Roo.dd.ScrollManager.register(this.el);
34822         }
34823         if((this.enableDD || this.enableDrop) && !this.dropZone){
34824            /**
34825             * The dropZone used by this tree if drop is enabled
34826             * @type Roo.tree.TreeDropZone
34827             */
34828              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34829                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34830            });
34831         }
34832         if((this.enableDD || this.enableDrag) && !this.dragZone){
34833            /**
34834             * The dragZone used by this tree if drag is enabled
34835             * @type Roo.tree.TreeDragZone
34836             */
34837             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34838                ddGroup: this.ddGroup || "TreeDD",
34839                scroll: this.ddScroll
34840            });
34841         }
34842         this.getSelectionModel().init(this);
34843         if (!this.root) {
34844             Roo.log("ROOT not set in tree");
34845             return this;
34846         }
34847         this.root.render();
34848         if(!this.rootVisible){
34849             this.root.renderChildren();
34850         }
34851         return this;
34852     }
34853 });/*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863  
34864
34865 /**
34866  * @class Roo.tree.DefaultSelectionModel
34867  * @extends Roo.util.Observable
34868  * The default single selection for a TreePanel.
34869  * @param {Object} cfg Configuration
34870  */
34871 Roo.tree.DefaultSelectionModel = function(cfg){
34872    this.selNode = null;
34873    
34874    
34875    
34876    this.addEvents({
34877        /**
34878         * @event selectionchange
34879         * Fires when the selected node changes
34880         * @param {DefaultSelectionModel} this
34881         * @param {TreeNode} node the new selection
34882         */
34883        "selectionchange" : true,
34884
34885        /**
34886         * @event beforeselect
34887         * Fires before the selected node changes, return false to cancel the change
34888         * @param {DefaultSelectionModel} this
34889         * @param {TreeNode} node the new selection
34890         * @param {TreeNode} node the old selection
34891         */
34892        "beforeselect" : true
34893    });
34894    
34895     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34896 };
34897
34898 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34899     init : function(tree){
34900         this.tree = tree;
34901         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34902         tree.on("click", this.onNodeClick, this);
34903     },
34904     
34905     onNodeClick : function(node, e){
34906         if (e.ctrlKey && this.selNode == node)  {
34907             this.unselect(node);
34908             return;
34909         }
34910         this.select(node);
34911     },
34912     
34913     /**
34914      * Select a node.
34915      * @param {TreeNode} node The node to select
34916      * @return {TreeNode} The selected node
34917      */
34918     select : function(node){
34919         var last = this.selNode;
34920         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34921             if(last){
34922                 last.ui.onSelectedChange(false);
34923             }
34924             this.selNode = node;
34925             node.ui.onSelectedChange(true);
34926             this.fireEvent("selectionchange", this, node, last);
34927         }
34928         return node;
34929     },
34930     
34931     /**
34932      * Deselect a node.
34933      * @param {TreeNode} node The node to unselect
34934      */
34935     unselect : function(node){
34936         if(this.selNode == node){
34937             this.clearSelections();
34938         }    
34939     },
34940     
34941     /**
34942      * Clear all selections
34943      */
34944     clearSelections : function(){
34945         var n = this.selNode;
34946         if(n){
34947             n.ui.onSelectedChange(false);
34948             this.selNode = null;
34949             this.fireEvent("selectionchange", this, null);
34950         }
34951         return n;
34952     },
34953     
34954     /**
34955      * Get the selected node
34956      * @return {TreeNode} The selected node
34957      */
34958     getSelectedNode : function(){
34959         return this.selNode;    
34960     },
34961     
34962     /**
34963      * Returns true if the node is selected
34964      * @param {TreeNode} node The node to check
34965      * @return {Boolean}
34966      */
34967     isSelected : function(node){
34968         return this.selNode == node;  
34969     },
34970
34971     /**
34972      * Selects the node above the selected node in the tree, intelligently walking the nodes
34973      * @return TreeNode The new selection
34974      */
34975     selectPrevious : function(){
34976         var s = this.selNode || this.lastSelNode;
34977         if(!s){
34978             return null;
34979         }
34980         var ps = s.previousSibling;
34981         if(ps){
34982             if(!ps.isExpanded() || ps.childNodes.length < 1){
34983                 return this.select(ps);
34984             } else{
34985                 var lc = ps.lastChild;
34986                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34987                     lc = lc.lastChild;
34988                 }
34989                 return this.select(lc);
34990             }
34991         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34992             return this.select(s.parentNode);
34993         }
34994         return null;
34995     },
34996
34997     /**
34998      * Selects the node above the selected node in the tree, intelligently walking the nodes
34999      * @return TreeNode The new selection
35000      */
35001     selectNext : function(){
35002         var s = this.selNode || this.lastSelNode;
35003         if(!s){
35004             return null;
35005         }
35006         if(s.firstChild && s.isExpanded()){
35007              return this.select(s.firstChild);
35008          }else if(s.nextSibling){
35009              return this.select(s.nextSibling);
35010          }else if(s.parentNode){
35011             var newS = null;
35012             s.parentNode.bubble(function(){
35013                 if(this.nextSibling){
35014                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35015                     return false;
35016                 }
35017             });
35018             return newS;
35019          }
35020         return null;
35021     },
35022
35023     onKeyDown : function(e){
35024         var s = this.selNode || this.lastSelNode;
35025         // undesirable, but required
35026         var sm = this;
35027         if(!s){
35028             return;
35029         }
35030         var k = e.getKey();
35031         switch(k){
35032              case e.DOWN:
35033                  e.stopEvent();
35034                  this.selectNext();
35035              break;
35036              case e.UP:
35037                  e.stopEvent();
35038                  this.selectPrevious();
35039              break;
35040              case e.RIGHT:
35041                  e.preventDefault();
35042                  if(s.hasChildNodes()){
35043                      if(!s.isExpanded()){
35044                          s.expand();
35045                      }else if(s.firstChild){
35046                          this.select(s.firstChild, e);
35047                      }
35048                  }
35049              break;
35050              case e.LEFT:
35051                  e.preventDefault();
35052                  if(s.hasChildNodes() && s.isExpanded()){
35053                      s.collapse();
35054                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35055                      this.select(s.parentNode, e);
35056                  }
35057              break;
35058         };
35059     }
35060 });
35061
35062 /**
35063  * @class Roo.tree.MultiSelectionModel
35064  * @extends Roo.util.Observable
35065  * Multi selection for a TreePanel.
35066  * @param {Object} cfg Configuration
35067  */
35068 Roo.tree.MultiSelectionModel = function(){
35069    this.selNodes = [];
35070    this.selMap = {};
35071    this.addEvents({
35072        /**
35073         * @event selectionchange
35074         * Fires when the selected nodes change
35075         * @param {MultiSelectionModel} this
35076         * @param {Array} nodes Array of the selected nodes
35077         */
35078        "selectionchange" : true
35079    });
35080    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35081    
35082 };
35083
35084 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35085     init : function(tree){
35086         this.tree = tree;
35087         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35088         tree.on("click", this.onNodeClick, this);
35089     },
35090     
35091     onNodeClick : function(node, e){
35092         this.select(node, e, e.ctrlKey);
35093     },
35094     
35095     /**
35096      * Select a node.
35097      * @param {TreeNode} node The node to select
35098      * @param {EventObject} e (optional) An event associated with the selection
35099      * @param {Boolean} keepExisting True to retain existing selections
35100      * @return {TreeNode} The selected node
35101      */
35102     select : function(node, e, keepExisting){
35103         if(keepExisting !== true){
35104             this.clearSelections(true);
35105         }
35106         if(this.isSelected(node)){
35107             this.lastSelNode = node;
35108             return node;
35109         }
35110         this.selNodes.push(node);
35111         this.selMap[node.id] = node;
35112         this.lastSelNode = node;
35113         node.ui.onSelectedChange(true);
35114         this.fireEvent("selectionchange", this, this.selNodes);
35115         return node;
35116     },
35117     
35118     /**
35119      * Deselect a node.
35120      * @param {TreeNode} node The node to unselect
35121      */
35122     unselect : function(node){
35123         if(this.selMap[node.id]){
35124             node.ui.onSelectedChange(false);
35125             var sn = this.selNodes;
35126             var index = -1;
35127             if(sn.indexOf){
35128                 index = sn.indexOf(node);
35129             }else{
35130                 for(var i = 0, len = sn.length; i < len; i++){
35131                     if(sn[i] == node){
35132                         index = i;
35133                         break;
35134                     }
35135                 }
35136             }
35137             if(index != -1){
35138                 this.selNodes.splice(index, 1);
35139             }
35140             delete this.selMap[node.id];
35141             this.fireEvent("selectionchange", this, this.selNodes);
35142         }
35143     },
35144     
35145     /**
35146      * Clear all selections
35147      */
35148     clearSelections : function(suppressEvent){
35149         var sn = this.selNodes;
35150         if(sn.length > 0){
35151             for(var i = 0, len = sn.length; i < len; i++){
35152                 sn[i].ui.onSelectedChange(false);
35153             }
35154             this.selNodes = [];
35155             this.selMap = {};
35156             if(suppressEvent !== true){
35157                 this.fireEvent("selectionchange", this, this.selNodes);
35158             }
35159         }
35160     },
35161     
35162     /**
35163      * Returns true if the node is selected
35164      * @param {TreeNode} node The node to check
35165      * @return {Boolean}
35166      */
35167     isSelected : function(node){
35168         return this.selMap[node.id] ? true : false;  
35169     },
35170     
35171     /**
35172      * Returns an array of the selected nodes
35173      * @return {Array}
35174      */
35175     getSelectedNodes : function(){
35176         return this.selNodes;    
35177     },
35178
35179     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35180
35181     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35182
35183     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35184 });/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194  
35195 /**
35196  * @class Roo.tree.TreeNode
35197  * @extends Roo.data.Node
35198  * @cfg {String} text The text for this node
35199  * @cfg {Boolean} expanded true to start the node expanded
35200  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35201  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35202  * @cfg {Boolean} disabled true to start the node disabled
35203  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35204  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35205  * @cfg {String} cls A css class to be added to the node
35206  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35207  * @cfg {String} href URL of the link used for the node (defaults to #)
35208  * @cfg {String} hrefTarget target frame for the link
35209  * @cfg {String} qtip An Ext QuickTip for the node
35210  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35211  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35212  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35213  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35214  * (defaults to undefined with no checkbox rendered)
35215  * @constructor
35216  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35217  */
35218 Roo.tree.TreeNode = function(attributes){
35219     attributes = attributes || {};
35220     if(typeof attributes == "string"){
35221         attributes = {text: attributes};
35222     }
35223     this.childrenRendered = false;
35224     this.rendered = false;
35225     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35226     this.expanded = attributes.expanded === true;
35227     this.isTarget = attributes.isTarget !== false;
35228     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35229     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35230
35231     /**
35232      * Read-only. The text for this node. To change it use setText().
35233      * @type String
35234      */
35235     this.text = attributes.text;
35236     /**
35237      * True if this node is disabled.
35238      * @type Boolean
35239      */
35240     this.disabled = attributes.disabled === true;
35241
35242     this.addEvents({
35243         /**
35244         * @event textchange
35245         * Fires when the text for this node is changed
35246         * @param {Node} this This node
35247         * @param {String} text The new text
35248         * @param {String} oldText The old text
35249         */
35250         "textchange" : true,
35251         /**
35252         * @event beforeexpand
35253         * Fires before this node is expanded, return false to cancel.
35254         * @param {Node} this This node
35255         * @param {Boolean} deep
35256         * @param {Boolean} anim
35257         */
35258         "beforeexpand" : true,
35259         /**
35260         * @event beforecollapse
35261         * Fires before this node is collapsed, return false to cancel.
35262         * @param {Node} this This node
35263         * @param {Boolean} deep
35264         * @param {Boolean} anim
35265         */
35266         "beforecollapse" : true,
35267         /**
35268         * @event expand
35269         * Fires when this node is expanded
35270         * @param {Node} this This node
35271         */
35272         "expand" : true,
35273         /**
35274         * @event disabledchange
35275         * Fires when the disabled status of this node changes
35276         * @param {Node} this This node
35277         * @param {Boolean} disabled
35278         */
35279         "disabledchange" : true,
35280         /**
35281         * @event collapse
35282         * Fires when this node is collapsed
35283         * @param {Node} this This node
35284         */
35285         "collapse" : true,
35286         /**
35287         * @event beforeclick
35288         * Fires before click processing. Return false to cancel the default action.
35289         * @param {Node} this This node
35290         * @param {Roo.EventObject} e The event object
35291         */
35292         "beforeclick":true,
35293         /**
35294         * @event checkchange
35295         * Fires when a node with a checkbox's checked property changes
35296         * @param {Node} this This node
35297         * @param {Boolean} checked
35298         */
35299         "checkchange":true,
35300         /**
35301         * @event click
35302         * Fires when this node is clicked
35303         * @param {Node} this This node
35304         * @param {Roo.EventObject} e The event object
35305         */
35306         "click":true,
35307         /**
35308         * @event dblclick
35309         * Fires when this node is double clicked
35310         * @param {Node} this This node
35311         * @param {Roo.EventObject} e The event object
35312         */
35313         "dblclick":true,
35314         /**
35315         * @event contextmenu
35316         * Fires when this node is right clicked
35317         * @param {Node} this This node
35318         * @param {Roo.EventObject} e The event object
35319         */
35320         "contextmenu":true,
35321         /**
35322         * @event beforechildrenrendered
35323         * Fires right before the child nodes for this node are rendered
35324         * @param {Node} this This node
35325         */
35326         "beforechildrenrendered":true
35327     });
35328
35329     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35330
35331     /**
35332      * Read-only. The UI for this node
35333      * @type TreeNodeUI
35334      */
35335     this.ui = new uiClass(this);
35336     
35337     // finally support items[]
35338     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35339         return;
35340     }
35341     
35342     
35343     Roo.each(this.attributes.items, function(c) {
35344         this.appendChild(Roo.factory(c,Roo.Tree));
35345     }, this);
35346     delete this.attributes.items;
35347     
35348     
35349     
35350 };
35351 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35352     preventHScroll: true,
35353     /**
35354      * Returns true if this node is expanded
35355      * @return {Boolean}
35356      */
35357     isExpanded : function(){
35358         return this.expanded;
35359     },
35360
35361     /**
35362      * Returns the UI object for this node
35363      * @return {TreeNodeUI}
35364      */
35365     getUI : function(){
35366         return this.ui;
35367     },
35368
35369     // private override
35370     setFirstChild : function(node){
35371         var of = this.firstChild;
35372         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35373         if(this.childrenRendered && of && node != of){
35374             of.renderIndent(true, true);
35375         }
35376         if(this.rendered){
35377             this.renderIndent(true, true);
35378         }
35379     },
35380
35381     // private override
35382     setLastChild : function(node){
35383         var ol = this.lastChild;
35384         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35385         if(this.childrenRendered && ol && node != ol){
35386             ol.renderIndent(true, true);
35387         }
35388         if(this.rendered){
35389             this.renderIndent(true, true);
35390         }
35391     },
35392
35393     // these methods are overridden to provide lazy rendering support
35394     // private override
35395     appendChild : function()
35396     {
35397         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35398         if(node && this.childrenRendered){
35399             node.render();
35400         }
35401         this.ui.updateExpandIcon();
35402         return node;
35403     },
35404
35405     // private override
35406     removeChild : function(node){
35407         this.ownerTree.getSelectionModel().unselect(node);
35408         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35409         // if it's been rendered remove dom node
35410         if(this.childrenRendered){
35411             node.ui.remove();
35412         }
35413         if(this.childNodes.length < 1){
35414             this.collapse(false, false);
35415         }else{
35416             this.ui.updateExpandIcon();
35417         }
35418         if(!this.firstChild) {
35419             this.childrenRendered = false;
35420         }
35421         return node;
35422     },
35423
35424     // private override
35425     insertBefore : function(node, refNode){
35426         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35427         if(newNode && refNode && this.childrenRendered){
35428             node.render();
35429         }
35430         this.ui.updateExpandIcon();
35431         return newNode;
35432     },
35433
35434     /**
35435      * Sets the text for this node
35436      * @param {String} text
35437      */
35438     setText : function(text){
35439         var oldText = this.text;
35440         this.text = text;
35441         this.attributes.text = text;
35442         if(this.rendered){ // event without subscribing
35443             this.ui.onTextChange(this, text, oldText);
35444         }
35445         this.fireEvent("textchange", this, text, oldText);
35446     },
35447
35448     /**
35449      * Triggers selection of this node
35450      */
35451     select : function(){
35452         this.getOwnerTree().getSelectionModel().select(this);
35453     },
35454
35455     /**
35456      * Triggers deselection of this node
35457      */
35458     unselect : function(){
35459         this.getOwnerTree().getSelectionModel().unselect(this);
35460     },
35461
35462     /**
35463      * Returns true if this node is selected
35464      * @return {Boolean}
35465      */
35466     isSelected : function(){
35467         return this.getOwnerTree().getSelectionModel().isSelected(this);
35468     },
35469
35470     /**
35471      * Expand this node.
35472      * @param {Boolean} deep (optional) True to expand all children as well
35473      * @param {Boolean} anim (optional) false to cancel the default animation
35474      * @param {Function} callback (optional) A callback to be called when
35475      * expanding this node completes (does not wait for deep expand to complete).
35476      * Called with 1 parameter, this node.
35477      */
35478     expand : function(deep, anim, callback){
35479         if(!this.expanded){
35480             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35481                 return;
35482             }
35483             if(!this.childrenRendered){
35484                 this.renderChildren();
35485             }
35486             this.expanded = true;
35487             
35488             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35489                 this.ui.animExpand(function(){
35490                     this.fireEvent("expand", this);
35491                     if(typeof callback == "function"){
35492                         callback(this);
35493                     }
35494                     if(deep === true){
35495                         this.expandChildNodes(true);
35496                     }
35497                 }.createDelegate(this));
35498                 return;
35499             }else{
35500                 this.ui.expand();
35501                 this.fireEvent("expand", this);
35502                 if(typeof callback == "function"){
35503                     callback(this);
35504                 }
35505             }
35506         }else{
35507            if(typeof callback == "function"){
35508                callback(this);
35509            }
35510         }
35511         if(deep === true){
35512             this.expandChildNodes(true);
35513         }
35514     },
35515
35516     isHiddenRoot : function(){
35517         return this.isRoot && !this.getOwnerTree().rootVisible;
35518     },
35519
35520     /**
35521      * Collapse this node.
35522      * @param {Boolean} deep (optional) True to collapse all children as well
35523      * @param {Boolean} anim (optional) false to cancel the default animation
35524      */
35525     collapse : function(deep, anim){
35526         if(this.expanded && !this.isHiddenRoot()){
35527             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35528                 return;
35529             }
35530             this.expanded = false;
35531             if((this.getOwnerTree().animate && anim !== false) || anim){
35532                 this.ui.animCollapse(function(){
35533                     this.fireEvent("collapse", this);
35534                     if(deep === true){
35535                         this.collapseChildNodes(true);
35536                     }
35537                 }.createDelegate(this));
35538                 return;
35539             }else{
35540                 this.ui.collapse();
35541                 this.fireEvent("collapse", this);
35542             }
35543         }
35544         if(deep === true){
35545             var cs = this.childNodes;
35546             for(var i = 0, len = cs.length; i < len; i++) {
35547                 cs[i].collapse(true, false);
35548             }
35549         }
35550     },
35551
35552     // private
35553     delayedExpand : function(delay){
35554         if(!this.expandProcId){
35555             this.expandProcId = this.expand.defer(delay, this);
35556         }
35557     },
35558
35559     // private
35560     cancelExpand : function(){
35561         if(this.expandProcId){
35562             clearTimeout(this.expandProcId);
35563         }
35564         this.expandProcId = false;
35565     },
35566
35567     /**
35568      * Toggles expanded/collapsed state of the node
35569      */
35570     toggle : function(){
35571         if(this.expanded){
35572             this.collapse();
35573         }else{
35574             this.expand();
35575         }
35576     },
35577
35578     /**
35579      * Ensures all parent nodes are expanded
35580      */
35581     ensureVisible : function(callback){
35582         var tree = this.getOwnerTree();
35583         tree.expandPath(this.parentNode.getPath(), false, function(){
35584             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35585             Roo.callback(callback);
35586         }.createDelegate(this));
35587     },
35588
35589     /**
35590      * Expand all child nodes
35591      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35592      */
35593     expandChildNodes : function(deep){
35594         var cs = this.childNodes;
35595         for(var i = 0, len = cs.length; i < len; i++) {
35596                 cs[i].expand(deep);
35597         }
35598     },
35599
35600     /**
35601      * Collapse all child nodes
35602      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35603      */
35604     collapseChildNodes : function(deep){
35605         var cs = this.childNodes;
35606         for(var i = 0, len = cs.length; i < len; i++) {
35607                 cs[i].collapse(deep);
35608         }
35609     },
35610
35611     /**
35612      * Disables this node
35613      */
35614     disable : function(){
35615         this.disabled = true;
35616         this.unselect();
35617         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35618             this.ui.onDisableChange(this, true);
35619         }
35620         this.fireEvent("disabledchange", this, true);
35621     },
35622
35623     /**
35624      * Enables this node
35625      */
35626     enable : function(){
35627         this.disabled = false;
35628         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35629             this.ui.onDisableChange(this, false);
35630         }
35631         this.fireEvent("disabledchange", this, false);
35632     },
35633
35634     // private
35635     renderChildren : function(suppressEvent){
35636         if(suppressEvent !== false){
35637             this.fireEvent("beforechildrenrendered", this);
35638         }
35639         var cs = this.childNodes;
35640         for(var i = 0, len = cs.length; i < len; i++){
35641             cs[i].render(true);
35642         }
35643         this.childrenRendered = true;
35644     },
35645
35646     // private
35647     sort : function(fn, scope){
35648         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35649         if(this.childrenRendered){
35650             var cs = this.childNodes;
35651             for(var i = 0, len = cs.length; i < len; i++){
35652                 cs[i].render(true);
35653             }
35654         }
35655     },
35656
35657     // private
35658     render : function(bulkRender){
35659         this.ui.render(bulkRender);
35660         if(!this.rendered){
35661             this.rendered = true;
35662             if(this.expanded){
35663                 this.expanded = false;
35664                 this.expand(false, false);
35665             }
35666         }
35667     },
35668
35669     // private
35670     renderIndent : function(deep, refresh){
35671         if(refresh){
35672             this.ui.childIndent = null;
35673         }
35674         this.ui.renderIndent();
35675         if(deep === true && this.childrenRendered){
35676             var cs = this.childNodes;
35677             for(var i = 0, len = cs.length; i < len; i++){
35678                 cs[i].renderIndent(true, refresh);
35679             }
35680         }
35681     }
35682 });/*
35683  * Based on:
35684  * Ext JS Library 1.1.1
35685  * Copyright(c) 2006-2007, Ext JS, LLC.
35686  *
35687  * Originally Released Under LGPL - original licence link has changed is not relivant.
35688  *
35689  * Fork - LGPL
35690  * <script type="text/javascript">
35691  */
35692  
35693 /**
35694  * @class Roo.tree.AsyncTreeNode
35695  * @extends Roo.tree.TreeNode
35696  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35697  * @constructor
35698  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35699  */
35700  Roo.tree.AsyncTreeNode = function(config){
35701     this.loaded = false;
35702     this.loading = false;
35703     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35704     /**
35705     * @event beforeload
35706     * Fires before this node is loaded, return false to cancel
35707     * @param {Node} this This node
35708     */
35709     this.addEvents({'beforeload':true, 'load': true});
35710     /**
35711     * @event load
35712     * Fires when this node is loaded
35713     * @param {Node} this This node
35714     */
35715     /**
35716      * The loader used by this node (defaults to using the tree's defined loader)
35717      * @type TreeLoader
35718      * @property loader
35719      */
35720 };
35721 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35722     expand : function(deep, anim, callback){
35723         if(this.loading){ // if an async load is already running, waiting til it's done
35724             var timer;
35725             var f = function(){
35726                 if(!this.loading){ // done loading
35727                     clearInterval(timer);
35728                     this.expand(deep, anim, callback);
35729                 }
35730             }.createDelegate(this);
35731             timer = setInterval(f, 200);
35732             return;
35733         }
35734         if(!this.loaded){
35735             if(this.fireEvent("beforeload", this) === false){
35736                 return;
35737             }
35738             this.loading = true;
35739             this.ui.beforeLoad(this);
35740             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35741             if(loader){
35742                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35743                 return;
35744             }
35745         }
35746         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35747     },
35748     
35749     /**
35750      * Returns true if this node is currently loading
35751      * @return {Boolean}
35752      */
35753     isLoading : function(){
35754         return this.loading;  
35755     },
35756     
35757     loadComplete : function(deep, anim, callback){
35758         this.loading = false;
35759         this.loaded = true;
35760         this.ui.afterLoad(this);
35761         this.fireEvent("load", this);
35762         this.expand(deep, anim, callback);
35763     },
35764     
35765     /**
35766      * Returns true if this node has been loaded
35767      * @return {Boolean}
35768      */
35769     isLoaded : function(){
35770         return this.loaded;
35771     },
35772     
35773     hasChildNodes : function(){
35774         if(!this.isLeaf() && !this.loaded){
35775             return true;
35776         }else{
35777             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35778         }
35779     },
35780
35781     /**
35782      * Trigger a reload for this node
35783      * @param {Function} callback
35784      */
35785     reload : function(callback){
35786         this.collapse(false, false);
35787         while(this.firstChild){
35788             this.removeChild(this.firstChild);
35789         }
35790         this.childrenRendered = false;
35791         this.loaded = false;
35792         if(this.isHiddenRoot()){
35793             this.expanded = false;
35794         }
35795         this.expand(false, false, callback);
35796     }
35797 });/*
35798  * Based on:
35799  * Ext JS Library 1.1.1
35800  * Copyright(c) 2006-2007, Ext JS, LLC.
35801  *
35802  * Originally Released Under LGPL - original licence link has changed is not relivant.
35803  *
35804  * Fork - LGPL
35805  * <script type="text/javascript">
35806  */
35807  
35808 /**
35809  * @class Roo.tree.TreeNodeUI
35810  * @constructor
35811  * @param {Object} node The node to render
35812  * The TreeNode UI implementation is separate from the
35813  * tree implementation. Unless you are customizing the tree UI,
35814  * you should never have to use this directly.
35815  */
35816 Roo.tree.TreeNodeUI = function(node){
35817     this.node = node;
35818     this.rendered = false;
35819     this.animating = false;
35820     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35821 };
35822
35823 Roo.tree.TreeNodeUI.prototype = {
35824     removeChild : function(node){
35825         if(this.rendered){
35826             this.ctNode.removeChild(node.ui.getEl());
35827         }
35828     },
35829
35830     beforeLoad : function(){
35831          this.addClass("x-tree-node-loading");
35832     },
35833
35834     afterLoad : function(){
35835          this.removeClass("x-tree-node-loading");
35836     },
35837
35838     onTextChange : function(node, text, oldText){
35839         if(this.rendered){
35840             this.textNode.innerHTML = text;
35841         }
35842     },
35843
35844     onDisableChange : function(node, state){
35845         this.disabled = state;
35846         if(state){
35847             this.addClass("x-tree-node-disabled");
35848         }else{
35849             this.removeClass("x-tree-node-disabled");
35850         }
35851     },
35852
35853     onSelectedChange : function(state){
35854         if(state){
35855             this.focus();
35856             this.addClass("x-tree-selected");
35857         }else{
35858             //this.blur();
35859             this.removeClass("x-tree-selected");
35860         }
35861     },
35862
35863     onMove : function(tree, node, oldParent, newParent, index, refNode){
35864         this.childIndent = null;
35865         if(this.rendered){
35866             var targetNode = newParent.ui.getContainer();
35867             if(!targetNode){//target not rendered
35868                 this.holder = document.createElement("div");
35869                 this.holder.appendChild(this.wrap);
35870                 return;
35871             }
35872             var insertBefore = refNode ? refNode.ui.getEl() : null;
35873             if(insertBefore){
35874                 targetNode.insertBefore(this.wrap, insertBefore);
35875             }else{
35876                 targetNode.appendChild(this.wrap);
35877             }
35878             this.node.renderIndent(true);
35879         }
35880     },
35881
35882     addClass : function(cls){
35883         if(this.elNode){
35884             Roo.fly(this.elNode).addClass(cls);
35885         }
35886     },
35887
35888     removeClass : function(cls){
35889         if(this.elNode){
35890             Roo.fly(this.elNode).removeClass(cls);
35891         }
35892     },
35893
35894     remove : function(){
35895         if(this.rendered){
35896             this.holder = document.createElement("div");
35897             this.holder.appendChild(this.wrap);
35898         }
35899     },
35900
35901     fireEvent : function(){
35902         return this.node.fireEvent.apply(this.node, arguments);
35903     },
35904
35905     initEvents : function(){
35906         this.node.on("move", this.onMove, this);
35907         var E = Roo.EventManager;
35908         var a = this.anchor;
35909
35910         var el = Roo.fly(a, '_treeui');
35911
35912         if(Roo.isOpera){ // opera render bug ignores the CSS
35913             el.setStyle("text-decoration", "none");
35914         }
35915
35916         el.on("click", this.onClick, this);
35917         el.on("dblclick", this.onDblClick, this);
35918
35919         if(this.checkbox){
35920             Roo.EventManager.on(this.checkbox,
35921                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35922         }
35923
35924         el.on("contextmenu", this.onContextMenu, this);
35925
35926         var icon = Roo.fly(this.iconNode);
35927         icon.on("click", this.onClick, this);
35928         icon.on("dblclick", this.onDblClick, this);
35929         icon.on("contextmenu", this.onContextMenu, this);
35930         E.on(this.ecNode, "click", this.ecClick, this, true);
35931
35932         if(this.node.disabled){
35933             this.addClass("x-tree-node-disabled");
35934         }
35935         if(this.node.hidden){
35936             this.addClass("x-tree-node-disabled");
35937         }
35938         var ot = this.node.getOwnerTree();
35939         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35940         if(dd && (!this.node.isRoot || ot.rootVisible)){
35941             Roo.dd.Registry.register(this.elNode, {
35942                 node: this.node,
35943                 handles: this.getDDHandles(),
35944                 isHandle: false
35945             });
35946         }
35947     },
35948
35949     getDDHandles : function(){
35950         return [this.iconNode, this.textNode];
35951     },
35952
35953     hide : function(){
35954         if(this.rendered){
35955             this.wrap.style.display = "none";
35956         }
35957     },
35958
35959     show : function(){
35960         if(this.rendered){
35961             this.wrap.style.display = "";
35962         }
35963     },
35964
35965     onContextMenu : function(e){
35966         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35967             e.preventDefault();
35968             this.focus();
35969             this.fireEvent("contextmenu", this.node, e);
35970         }
35971     },
35972
35973     onClick : function(e){
35974         if(this.dropping){
35975             e.stopEvent();
35976             return;
35977         }
35978         if(this.fireEvent("beforeclick", this.node, e) !== false){
35979             if(!this.disabled && this.node.attributes.href){
35980                 this.fireEvent("click", this.node, e);
35981                 return;
35982             }
35983             e.preventDefault();
35984             if(this.disabled){
35985                 return;
35986             }
35987
35988             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35989                 this.node.toggle();
35990             }
35991
35992             this.fireEvent("click", this.node, e);
35993         }else{
35994             e.stopEvent();
35995         }
35996     },
35997
35998     onDblClick : function(e){
35999         e.preventDefault();
36000         if(this.disabled){
36001             return;
36002         }
36003         if(this.checkbox){
36004             this.toggleCheck();
36005         }
36006         if(!this.animating && this.node.hasChildNodes()){
36007             this.node.toggle();
36008         }
36009         this.fireEvent("dblclick", this.node, e);
36010     },
36011
36012     onCheckChange : function(){
36013         var checked = this.checkbox.checked;
36014         this.node.attributes.checked = checked;
36015         this.fireEvent('checkchange', this.node, checked);
36016     },
36017
36018     ecClick : function(e){
36019         if(!this.animating && this.node.hasChildNodes()){
36020             this.node.toggle();
36021         }
36022     },
36023
36024     startDrop : function(){
36025         this.dropping = true;
36026     },
36027
36028     // delayed drop so the click event doesn't get fired on a drop
36029     endDrop : function(){
36030        setTimeout(function(){
36031            this.dropping = false;
36032        }.createDelegate(this), 50);
36033     },
36034
36035     expand : function(){
36036         this.updateExpandIcon();
36037         this.ctNode.style.display = "";
36038     },
36039
36040     focus : function(){
36041         if(!this.node.preventHScroll){
36042             try{this.anchor.focus();
36043             }catch(e){}
36044         }else if(!Roo.isIE){
36045             try{
36046                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36047                 var l = noscroll.scrollLeft;
36048                 this.anchor.focus();
36049                 noscroll.scrollLeft = l;
36050             }catch(e){}
36051         }
36052     },
36053
36054     toggleCheck : function(value){
36055         var cb = this.checkbox;
36056         if(cb){
36057             cb.checked = (value === undefined ? !cb.checked : value);
36058         }
36059     },
36060
36061     blur : function(){
36062         try{
36063             this.anchor.blur();
36064         }catch(e){}
36065     },
36066
36067     animExpand : function(callback){
36068         var ct = Roo.get(this.ctNode);
36069         ct.stopFx();
36070         if(!this.node.hasChildNodes()){
36071             this.updateExpandIcon();
36072             this.ctNode.style.display = "";
36073             Roo.callback(callback);
36074             return;
36075         }
36076         this.animating = true;
36077         this.updateExpandIcon();
36078
36079         ct.slideIn('t', {
36080            callback : function(){
36081                this.animating = false;
36082                Roo.callback(callback);
36083             },
36084             scope: this,
36085             duration: this.node.ownerTree.duration || .25
36086         });
36087     },
36088
36089     highlight : function(){
36090         var tree = this.node.getOwnerTree();
36091         Roo.fly(this.wrap).highlight(
36092             tree.hlColor || "C3DAF9",
36093             {endColor: tree.hlBaseColor}
36094         );
36095     },
36096
36097     collapse : function(){
36098         this.updateExpandIcon();
36099         this.ctNode.style.display = "none";
36100     },
36101
36102     animCollapse : function(callback){
36103         var ct = Roo.get(this.ctNode);
36104         ct.enableDisplayMode('block');
36105         ct.stopFx();
36106
36107         this.animating = true;
36108         this.updateExpandIcon();
36109
36110         ct.slideOut('t', {
36111             callback : function(){
36112                this.animating = false;
36113                Roo.callback(callback);
36114             },
36115             scope: this,
36116             duration: this.node.ownerTree.duration || .25
36117         });
36118     },
36119
36120     getContainer : function(){
36121         return this.ctNode;
36122     },
36123
36124     getEl : function(){
36125         return this.wrap;
36126     },
36127
36128     appendDDGhost : function(ghostNode){
36129         ghostNode.appendChild(this.elNode.cloneNode(true));
36130     },
36131
36132     getDDRepairXY : function(){
36133         return Roo.lib.Dom.getXY(this.iconNode);
36134     },
36135
36136     onRender : function(){
36137         this.render();
36138     },
36139
36140     render : function(bulkRender){
36141         var n = this.node, a = n.attributes;
36142         var targetNode = n.parentNode ?
36143               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36144
36145         if(!this.rendered){
36146             this.rendered = true;
36147
36148             this.renderElements(n, a, targetNode, bulkRender);
36149
36150             if(a.qtip){
36151                if(this.textNode.setAttributeNS){
36152                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36153                    if(a.qtipTitle){
36154                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36155                    }
36156                }else{
36157                    this.textNode.setAttribute("ext:qtip", a.qtip);
36158                    if(a.qtipTitle){
36159                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36160                    }
36161                }
36162             }else if(a.qtipCfg){
36163                 a.qtipCfg.target = Roo.id(this.textNode);
36164                 Roo.QuickTips.register(a.qtipCfg);
36165             }
36166             this.initEvents();
36167             if(!this.node.expanded){
36168                 this.updateExpandIcon();
36169             }
36170         }else{
36171             if(bulkRender === true) {
36172                 targetNode.appendChild(this.wrap);
36173             }
36174         }
36175     },
36176
36177     renderElements : function(n, a, targetNode, bulkRender)
36178     {
36179         // add some indent caching, this helps performance when rendering a large tree
36180         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36181         var t = n.getOwnerTree();
36182         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36183         if (typeof(n.attributes.html) != 'undefined') {
36184             txt = n.attributes.html;
36185         }
36186         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36187         var cb = typeof a.checked == 'boolean';
36188         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36189         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36190             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36191             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36192             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36193             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36194             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36195              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36196                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36197             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36198             "</li>"];
36199
36200         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36201             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36202                                 n.nextSibling.ui.getEl(), buf.join(""));
36203         }else{
36204             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36205         }
36206
36207         this.elNode = this.wrap.childNodes[0];
36208         this.ctNode = this.wrap.childNodes[1];
36209         var cs = this.elNode.childNodes;
36210         this.indentNode = cs[0];
36211         this.ecNode = cs[1];
36212         this.iconNode = cs[2];
36213         var index = 3;
36214         if(cb){
36215             this.checkbox = cs[3];
36216             index++;
36217         }
36218         this.anchor = cs[index];
36219         this.textNode = cs[index].firstChild;
36220     },
36221
36222     getAnchor : function(){
36223         return this.anchor;
36224     },
36225
36226     getTextEl : function(){
36227         return this.textNode;
36228     },
36229
36230     getIconEl : function(){
36231         return this.iconNode;
36232     },
36233
36234     isChecked : function(){
36235         return this.checkbox ? this.checkbox.checked : false;
36236     },
36237
36238     updateExpandIcon : function(){
36239         if(this.rendered){
36240             var n = this.node, c1, c2;
36241             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36242             var hasChild = n.hasChildNodes();
36243             if(hasChild){
36244                 if(n.expanded){
36245                     cls += "-minus";
36246                     c1 = "x-tree-node-collapsed";
36247                     c2 = "x-tree-node-expanded";
36248                 }else{
36249                     cls += "-plus";
36250                     c1 = "x-tree-node-expanded";
36251                     c2 = "x-tree-node-collapsed";
36252                 }
36253                 if(this.wasLeaf){
36254                     this.removeClass("x-tree-node-leaf");
36255                     this.wasLeaf = false;
36256                 }
36257                 if(this.c1 != c1 || this.c2 != c2){
36258                     Roo.fly(this.elNode).replaceClass(c1, c2);
36259                     this.c1 = c1; this.c2 = c2;
36260                 }
36261             }else{
36262                 // this changes non-leafs into leafs if they have no children.
36263                 // it's not very rational behaviour..
36264                 
36265                 if(!this.wasLeaf && this.node.leaf){
36266                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36267                     delete this.c1;
36268                     delete this.c2;
36269                     this.wasLeaf = true;
36270                 }
36271             }
36272             var ecc = "x-tree-ec-icon "+cls;
36273             if(this.ecc != ecc){
36274                 this.ecNode.className = ecc;
36275                 this.ecc = ecc;
36276             }
36277         }
36278     },
36279
36280     getChildIndent : function(){
36281         if(!this.childIndent){
36282             var buf = [];
36283             var p = this.node;
36284             while(p){
36285                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36286                     if(!p.isLast()) {
36287                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36288                     } else {
36289                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36290                     }
36291                 }
36292                 p = p.parentNode;
36293             }
36294             this.childIndent = buf.join("");
36295         }
36296         return this.childIndent;
36297     },
36298
36299     renderIndent : function(){
36300         if(this.rendered){
36301             var indent = "";
36302             var p = this.node.parentNode;
36303             if(p){
36304                 indent = p.ui.getChildIndent();
36305             }
36306             if(this.indentMarkup != indent){ // don't rerender if not required
36307                 this.indentNode.innerHTML = indent;
36308                 this.indentMarkup = indent;
36309             }
36310             this.updateExpandIcon();
36311         }
36312     }
36313 };
36314
36315 Roo.tree.RootTreeNodeUI = function(){
36316     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36317 };
36318 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36319     render : function(){
36320         if(!this.rendered){
36321             var targetNode = this.node.ownerTree.innerCt.dom;
36322             this.node.expanded = true;
36323             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36324             this.wrap = this.ctNode = targetNode.firstChild;
36325         }
36326     },
36327     collapse : function(){
36328     },
36329     expand : function(){
36330     }
36331 });/*
36332  * Based on:
36333  * Ext JS Library 1.1.1
36334  * Copyright(c) 2006-2007, Ext JS, LLC.
36335  *
36336  * Originally Released Under LGPL - original licence link has changed is not relivant.
36337  *
36338  * Fork - LGPL
36339  * <script type="text/javascript">
36340  */
36341 /**
36342  * @class Roo.tree.TreeLoader
36343  * @extends Roo.util.Observable
36344  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36345  * nodes from a specified URL. The response must be a javascript Array definition
36346  * who's elements are node definition objects. eg:
36347  * <pre><code>
36348 {  success : true,
36349    data :      [
36350    
36351     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36352     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36353     ]
36354 }
36355
36356
36357 </code></pre>
36358  * <br><br>
36359  * The old style respose with just an array is still supported, but not recommended.
36360  * <br><br>
36361  *
36362  * A server request is sent, and child nodes are loaded only when a node is expanded.
36363  * The loading node's id is passed to the server under the parameter name "node" to
36364  * enable the server to produce the correct child nodes.
36365  * <br><br>
36366  * To pass extra parameters, an event handler may be attached to the "beforeload"
36367  * event, and the parameters specified in the TreeLoader's baseParams property:
36368  * <pre><code>
36369     myTreeLoader.on("beforeload", function(treeLoader, node) {
36370         this.baseParams.category = node.attributes.category;
36371     }, this);
36372     
36373 </code></pre>
36374  *
36375  * This would pass an HTTP parameter called "category" to the server containing
36376  * the value of the Node's "category" attribute.
36377  * @constructor
36378  * Creates a new Treeloader.
36379  * @param {Object} config A config object containing config properties.
36380  */
36381 Roo.tree.TreeLoader = function(config){
36382     this.baseParams = {};
36383     this.requestMethod = "POST";
36384     Roo.apply(this, config);
36385
36386     this.addEvents({
36387     
36388         /**
36389          * @event beforeload
36390          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36391          * @param {Object} This TreeLoader object.
36392          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36393          * @param {Object} callback The callback function specified in the {@link #load} call.
36394          */
36395         beforeload : true,
36396         /**
36397          * @event load
36398          * Fires when the node has been successfuly loaded.
36399          * @param {Object} This TreeLoader object.
36400          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36401          * @param {Object} response The response object containing the data from the server.
36402          */
36403         load : true,
36404         /**
36405          * @event loadexception
36406          * Fires if the network request failed.
36407          * @param {Object} This TreeLoader object.
36408          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36409          * @param {Object} response The response object containing the data from the server.
36410          */
36411         loadexception : true,
36412         /**
36413          * @event create
36414          * Fires before a node is created, enabling you to return custom Node types 
36415          * @param {Object} This TreeLoader object.
36416          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36417          */
36418         create : true
36419     });
36420
36421     Roo.tree.TreeLoader.superclass.constructor.call(this);
36422 };
36423
36424 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36425     /**
36426     * @cfg {String} dataUrl The URL from which to request a Json string which
36427     * specifies an array of node definition object representing the child nodes
36428     * to be loaded.
36429     */
36430     /**
36431     * @cfg {String} requestMethod either GET or POST
36432     * defaults to POST (due to BC)
36433     * to be loaded.
36434     */
36435     /**
36436     * @cfg {Object} baseParams (optional) An object containing properties which
36437     * specify HTTP parameters to be passed to each request for child nodes.
36438     */
36439     /**
36440     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36441     * created by this loader. If the attributes sent by the server have an attribute in this object,
36442     * they take priority.
36443     */
36444     /**
36445     * @cfg {Object} uiProviders (optional) An object containing properties which
36446     * 
36447     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36448     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36449     * <i>uiProvider</i> attribute of a returned child node is a string rather
36450     * than a reference to a TreeNodeUI implementation, this that string value
36451     * is used as a property name in the uiProviders object. You can define the provider named
36452     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36453     */
36454     uiProviders : {},
36455
36456     /**
36457     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36458     * child nodes before loading.
36459     */
36460     clearOnLoad : true,
36461
36462     /**
36463     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36464     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36465     * Grid query { data : [ .....] }
36466     */
36467     
36468     root : false,
36469      /**
36470     * @cfg {String} queryParam (optional) 
36471     * Name of the query as it will be passed on the querystring (defaults to 'node')
36472     * eg. the request will be ?node=[id]
36473     */
36474     
36475     
36476     queryParam: false,
36477     
36478     /**
36479      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36480      * This is called automatically when a node is expanded, but may be used to reload
36481      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36482      * @param {Roo.tree.TreeNode} node
36483      * @param {Function} callback
36484      */
36485     load : function(node, callback){
36486         if(this.clearOnLoad){
36487             while(node.firstChild){
36488                 node.removeChild(node.firstChild);
36489             }
36490         }
36491         if(node.attributes.children){ // preloaded json children
36492             var cs = node.attributes.children;
36493             for(var i = 0, len = cs.length; i < len; i++){
36494                 node.appendChild(this.createNode(cs[i]));
36495             }
36496             if(typeof callback == "function"){
36497                 callback();
36498             }
36499         }else if(this.dataUrl){
36500             this.requestData(node, callback);
36501         }
36502     },
36503
36504     getParams: function(node){
36505         var buf = [], bp = this.baseParams;
36506         for(var key in bp){
36507             if(typeof bp[key] != "function"){
36508                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36509             }
36510         }
36511         var n = this.queryParam === false ? 'node' : this.queryParam;
36512         buf.push(n + "=", encodeURIComponent(node.id));
36513         return buf.join("");
36514     },
36515
36516     requestData : function(node, callback){
36517         if(this.fireEvent("beforeload", this, node, callback) !== false){
36518             this.transId = Roo.Ajax.request({
36519                 method:this.requestMethod,
36520                 url: this.dataUrl||this.url,
36521                 success: this.handleResponse,
36522                 failure: this.handleFailure,
36523                 scope: this,
36524                 argument: {callback: callback, node: node},
36525                 params: this.getParams(node)
36526             });
36527         }else{
36528             // if the load is cancelled, make sure we notify
36529             // the node that we are done
36530             if(typeof callback == "function"){
36531                 callback();
36532             }
36533         }
36534     },
36535
36536     isLoading : function(){
36537         return this.transId ? true : false;
36538     },
36539
36540     abort : function(){
36541         if(this.isLoading()){
36542             Roo.Ajax.abort(this.transId);
36543         }
36544     },
36545
36546     // private
36547     createNode : function(attr)
36548     {
36549         // apply baseAttrs, nice idea Corey!
36550         if(this.baseAttrs){
36551             Roo.applyIf(attr, this.baseAttrs);
36552         }
36553         if(this.applyLoader !== false){
36554             attr.loader = this;
36555         }
36556         // uiProvider = depreciated..
36557         
36558         if(typeof(attr.uiProvider) == 'string'){
36559            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36560                 /**  eval:var:attr */ eval(attr.uiProvider);
36561         }
36562         if(typeof(this.uiProviders['default']) != 'undefined') {
36563             attr.uiProvider = this.uiProviders['default'];
36564         }
36565         
36566         this.fireEvent('create', this, attr);
36567         
36568         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36569         return(attr.leaf ?
36570                         new Roo.tree.TreeNode(attr) :
36571                         new Roo.tree.AsyncTreeNode(attr));
36572     },
36573
36574     processResponse : function(response, node, callback)
36575     {
36576         var json = response.responseText;
36577         try {
36578             
36579             var o = Roo.decode(json);
36580             
36581             if (this.root === false && typeof(o.success) != undefined) {
36582                 this.root = 'data'; // the default behaviour for list like data..
36583                 }
36584                 
36585             if (this.root !== false &&  !o.success) {
36586                 // it's a failure condition.
36587                 var a = response.argument;
36588                 this.fireEvent("loadexception", this, a.node, response);
36589                 Roo.log("Load failed - should have a handler really");
36590                 return;
36591             }
36592             
36593             
36594             
36595             if (this.root !== false) {
36596                  o = o[this.root];
36597             }
36598             
36599             for(var i = 0, len = o.length; i < len; i++){
36600                 var n = this.createNode(o[i]);
36601                 if(n){
36602                     node.appendChild(n);
36603                 }
36604             }
36605             if(typeof callback == "function"){
36606                 callback(this, node);
36607             }
36608         }catch(e){
36609             this.handleFailure(response);
36610         }
36611     },
36612
36613     handleResponse : function(response){
36614         this.transId = false;
36615         var a = response.argument;
36616         this.processResponse(response, a.node, a.callback);
36617         this.fireEvent("load", this, a.node, response);
36618     },
36619
36620     handleFailure : function(response)
36621     {
36622         // should handle failure better..
36623         this.transId = false;
36624         var a = response.argument;
36625         this.fireEvent("loadexception", this, a.node, response);
36626         if(typeof a.callback == "function"){
36627             a.callback(this, a.node);
36628         }
36629     }
36630 });/*
36631  * Based on:
36632  * Ext JS Library 1.1.1
36633  * Copyright(c) 2006-2007, Ext JS, LLC.
36634  *
36635  * Originally Released Under LGPL - original licence link has changed is not relivant.
36636  *
36637  * Fork - LGPL
36638  * <script type="text/javascript">
36639  */
36640
36641 /**
36642 * @class Roo.tree.TreeFilter
36643 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36644 * @param {TreePanel} tree
36645 * @param {Object} config (optional)
36646  */
36647 Roo.tree.TreeFilter = function(tree, config){
36648     this.tree = tree;
36649     this.filtered = {};
36650     Roo.apply(this, config);
36651 };
36652
36653 Roo.tree.TreeFilter.prototype = {
36654     clearBlank:false,
36655     reverse:false,
36656     autoClear:false,
36657     remove:false,
36658
36659      /**
36660      * Filter the data by a specific attribute.
36661      * @param {String/RegExp} value Either string that the attribute value
36662      * should start with or a RegExp to test against the attribute
36663      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36664      * @param {TreeNode} startNode (optional) The node to start the filter at.
36665      */
36666     filter : function(value, attr, startNode){
36667         attr = attr || "text";
36668         var f;
36669         if(typeof value == "string"){
36670             var vlen = value.length;
36671             // auto clear empty filter
36672             if(vlen == 0 && this.clearBlank){
36673                 this.clear();
36674                 return;
36675             }
36676             value = value.toLowerCase();
36677             f = function(n){
36678                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36679             };
36680         }else if(value.exec){ // regex?
36681             f = function(n){
36682                 return value.test(n.attributes[attr]);
36683             };
36684         }else{
36685             throw 'Illegal filter type, must be string or regex';
36686         }
36687         this.filterBy(f, null, startNode);
36688         },
36689
36690     /**
36691      * Filter by a function. The passed function will be called with each
36692      * node in the tree (or from the startNode). If the function returns true, the node is kept
36693      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36694      * @param {Function} fn The filter function
36695      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36696      */
36697     filterBy : function(fn, scope, startNode){
36698         startNode = startNode || this.tree.root;
36699         if(this.autoClear){
36700             this.clear();
36701         }
36702         var af = this.filtered, rv = this.reverse;
36703         var f = function(n){
36704             if(n == startNode){
36705                 return true;
36706             }
36707             if(af[n.id]){
36708                 return false;
36709             }
36710             var m = fn.call(scope || n, n);
36711             if(!m || rv){
36712                 af[n.id] = n;
36713                 n.ui.hide();
36714                 return false;
36715             }
36716             return true;
36717         };
36718         startNode.cascade(f);
36719         if(this.remove){
36720            for(var id in af){
36721                if(typeof id != "function"){
36722                    var n = af[id];
36723                    if(n && n.parentNode){
36724                        n.parentNode.removeChild(n);
36725                    }
36726                }
36727            }
36728         }
36729     },
36730
36731     /**
36732      * Clears the current filter. Note: with the "remove" option
36733      * set a filter cannot be cleared.
36734      */
36735     clear : function(){
36736         var t = this.tree;
36737         var af = this.filtered;
36738         for(var id in af){
36739             if(typeof id != "function"){
36740                 var n = af[id];
36741                 if(n){
36742                     n.ui.show();
36743                 }
36744             }
36745         }
36746         this.filtered = {};
36747     }
36748 };
36749 /*
36750  * Based on:
36751  * Ext JS Library 1.1.1
36752  * Copyright(c) 2006-2007, Ext JS, LLC.
36753  *
36754  * Originally Released Under LGPL - original licence link has changed is not relivant.
36755  *
36756  * Fork - LGPL
36757  * <script type="text/javascript">
36758  */
36759  
36760
36761 /**
36762  * @class Roo.tree.TreeSorter
36763  * Provides sorting of nodes in a TreePanel
36764  * 
36765  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36766  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36767  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36768  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36769  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36770  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36771  * @constructor
36772  * @param {TreePanel} tree
36773  * @param {Object} config
36774  */
36775 Roo.tree.TreeSorter = function(tree, config){
36776     Roo.apply(this, config);
36777     tree.on("beforechildrenrendered", this.doSort, this);
36778     tree.on("append", this.updateSort, this);
36779     tree.on("insert", this.updateSort, this);
36780     
36781     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36782     var p = this.property || "text";
36783     var sortType = this.sortType;
36784     var fs = this.folderSort;
36785     var cs = this.caseSensitive === true;
36786     var leafAttr = this.leafAttr || 'leaf';
36787
36788     this.sortFn = function(n1, n2){
36789         if(fs){
36790             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36791                 return 1;
36792             }
36793             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36794                 return -1;
36795             }
36796         }
36797         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36798         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36799         if(v1 < v2){
36800                         return dsc ? +1 : -1;
36801                 }else if(v1 > v2){
36802                         return dsc ? -1 : +1;
36803         }else{
36804                 return 0;
36805         }
36806     };
36807 };
36808
36809 Roo.tree.TreeSorter.prototype = {
36810     doSort : function(node){
36811         node.sort(this.sortFn);
36812     },
36813     
36814     compareNodes : function(n1, n2){
36815         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36816     },
36817     
36818     updateSort : function(tree, node){
36819         if(node.childrenRendered){
36820             this.doSort.defer(1, this, [node]);
36821         }
36822     }
36823 };/*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833
36834 if(Roo.dd.DropZone){
36835     
36836 Roo.tree.TreeDropZone = function(tree, config){
36837     this.allowParentInsert = false;
36838     this.allowContainerDrop = false;
36839     this.appendOnly = false;
36840     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36841     this.tree = tree;
36842     this.lastInsertClass = "x-tree-no-status";
36843     this.dragOverData = {};
36844 };
36845
36846 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36847     ddGroup : "TreeDD",
36848     scroll:  true,
36849     
36850     expandDelay : 1000,
36851     
36852     expandNode : function(node){
36853         if(node.hasChildNodes() && !node.isExpanded()){
36854             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36855         }
36856     },
36857     
36858     queueExpand : function(node){
36859         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36860     },
36861     
36862     cancelExpand : function(){
36863         if(this.expandProcId){
36864             clearTimeout(this.expandProcId);
36865             this.expandProcId = false;
36866         }
36867     },
36868     
36869     isValidDropPoint : function(n, pt, dd, e, data){
36870         if(!n || !data){ return false; }
36871         var targetNode = n.node;
36872         var dropNode = data.node;
36873         // default drop rules
36874         if(!(targetNode && targetNode.isTarget && pt)){
36875             return false;
36876         }
36877         if(pt == "append" && targetNode.allowChildren === false){
36878             return false;
36879         }
36880         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36881             return false;
36882         }
36883         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36884             return false;
36885         }
36886         // reuse the object
36887         var overEvent = this.dragOverData;
36888         overEvent.tree = this.tree;
36889         overEvent.target = targetNode;
36890         overEvent.data = data;
36891         overEvent.point = pt;
36892         overEvent.source = dd;
36893         overEvent.rawEvent = e;
36894         overEvent.dropNode = dropNode;
36895         overEvent.cancel = false;  
36896         var result = this.tree.fireEvent("nodedragover", overEvent);
36897         return overEvent.cancel === false && result !== false;
36898     },
36899     
36900     getDropPoint : function(e, n, dd)
36901     {
36902         var tn = n.node;
36903         if(tn.isRoot){
36904             return tn.allowChildren !== false ? "append" : false; // always append for root
36905         }
36906         var dragEl = n.ddel;
36907         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36908         var y = Roo.lib.Event.getPageY(e);
36909         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36910         
36911         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36912         var noAppend = tn.allowChildren === false;
36913         if(this.appendOnly || tn.parentNode.allowChildren === false){
36914             return noAppend ? false : "append";
36915         }
36916         var noBelow = false;
36917         if(!this.allowParentInsert){
36918             noBelow = tn.hasChildNodes() && tn.isExpanded();
36919         }
36920         var q = (b - t) / (noAppend ? 2 : 3);
36921         if(y >= t && y < (t + q)){
36922             return "above";
36923         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36924             return "below";
36925         }else{
36926             return "append";
36927         }
36928     },
36929     
36930     onNodeEnter : function(n, dd, e, data)
36931     {
36932         this.cancelExpand();
36933     },
36934     
36935     onNodeOver : function(n, dd, e, data)
36936     {
36937        
36938         var pt = this.getDropPoint(e, n, dd);
36939         var node = n.node;
36940         
36941         // auto node expand check
36942         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36943             this.queueExpand(node);
36944         }else if(pt != "append"){
36945             this.cancelExpand();
36946         }
36947         
36948         // set the insert point style on the target node
36949         var returnCls = this.dropNotAllowed;
36950         if(this.isValidDropPoint(n, pt, dd, e, data)){
36951            if(pt){
36952                var el = n.ddel;
36953                var cls;
36954                if(pt == "above"){
36955                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36956                    cls = "x-tree-drag-insert-above";
36957                }else if(pt == "below"){
36958                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36959                    cls = "x-tree-drag-insert-below";
36960                }else{
36961                    returnCls = "x-tree-drop-ok-append";
36962                    cls = "x-tree-drag-append";
36963                }
36964                if(this.lastInsertClass != cls){
36965                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36966                    this.lastInsertClass = cls;
36967                }
36968            }
36969        }
36970        return returnCls;
36971     },
36972     
36973     onNodeOut : function(n, dd, e, data){
36974         
36975         this.cancelExpand();
36976         this.removeDropIndicators(n);
36977     },
36978     
36979     onNodeDrop : function(n, dd, e, data){
36980         var point = this.getDropPoint(e, n, dd);
36981         var targetNode = n.node;
36982         targetNode.ui.startDrop();
36983         if(!this.isValidDropPoint(n, point, dd, e, data)){
36984             targetNode.ui.endDrop();
36985             return false;
36986         }
36987         // first try to find the drop node
36988         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36989         var dropEvent = {
36990             tree : this.tree,
36991             target: targetNode,
36992             data: data,
36993             point: point,
36994             source: dd,
36995             rawEvent: e,
36996             dropNode: dropNode,
36997             cancel: !dropNode   
36998         };
36999         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37000         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37001             targetNode.ui.endDrop();
37002             return false;
37003         }
37004         // allow target changing
37005         targetNode = dropEvent.target;
37006         if(point == "append" && !targetNode.isExpanded()){
37007             targetNode.expand(false, null, function(){
37008                 this.completeDrop(dropEvent);
37009             }.createDelegate(this));
37010         }else{
37011             this.completeDrop(dropEvent);
37012         }
37013         return true;
37014     },
37015     
37016     completeDrop : function(de){
37017         var ns = de.dropNode, p = de.point, t = de.target;
37018         if(!(ns instanceof Array)){
37019             ns = [ns];
37020         }
37021         var n;
37022         for(var i = 0, len = ns.length; i < len; i++){
37023             n = ns[i];
37024             if(p == "above"){
37025                 t.parentNode.insertBefore(n, t);
37026             }else if(p == "below"){
37027                 t.parentNode.insertBefore(n, t.nextSibling);
37028             }else{
37029                 t.appendChild(n);
37030             }
37031         }
37032         n.ui.focus();
37033         if(this.tree.hlDrop){
37034             n.ui.highlight();
37035         }
37036         t.ui.endDrop();
37037         this.tree.fireEvent("nodedrop", de);
37038     },
37039     
37040     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37041         if(this.tree.hlDrop){
37042             dropNode.ui.focus();
37043             dropNode.ui.highlight();
37044         }
37045         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37046     },
37047     
37048     getTree : function(){
37049         return this.tree;
37050     },
37051     
37052     removeDropIndicators : function(n){
37053         if(n && n.ddel){
37054             var el = n.ddel;
37055             Roo.fly(el).removeClass([
37056                     "x-tree-drag-insert-above",
37057                     "x-tree-drag-insert-below",
37058                     "x-tree-drag-append"]);
37059             this.lastInsertClass = "_noclass";
37060         }
37061     },
37062     
37063     beforeDragDrop : function(target, e, id){
37064         this.cancelExpand();
37065         return true;
37066     },
37067     
37068     afterRepair : function(data){
37069         if(data && Roo.enableFx){
37070             data.node.ui.highlight();
37071         }
37072         this.hideProxy();
37073     } 
37074     
37075 });
37076
37077 }
37078 /*
37079  * Based on:
37080  * Ext JS Library 1.1.1
37081  * Copyright(c) 2006-2007, Ext JS, LLC.
37082  *
37083  * Originally Released Under LGPL - original licence link has changed is not relivant.
37084  *
37085  * Fork - LGPL
37086  * <script type="text/javascript">
37087  */
37088  
37089
37090 if(Roo.dd.DragZone){
37091 Roo.tree.TreeDragZone = function(tree, config){
37092     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37093     this.tree = tree;
37094 };
37095
37096 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37097     ddGroup : "TreeDD",
37098    
37099     onBeforeDrag : function(data, e){
37100         var n = data.node;
37101         return n && n.draggable && !n.disabled;
37102     },
37103      
37104     
37105     onInitDrag : function(e){
37106         var data = this.dragData;
37107         this.tree.getSelectionModel().select(data.node);
37108         this.proxy.update("");
37109         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37110         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37111     },
37112     
37113     getRepairXY : function(e, data){
37114         return data.node.ui.getDDRepairXY();
37115     },
37116     
37117     onEndDrag : function(data, e){
37118         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37119         
37120         
37121     },
37122     
37123     onValidDrop : function(dd, e, id){
37124         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37125         this.hideProxy();
37126     },
37127     
37128     beforeInvalidDrop : function(e, id){
37129         // this scrolls the original position back into view
37130         var sm = this.tree.getSelectionModel();
37131         sm.clearSelections();
37132         sm.select(this.dragData.node);
37133     }
37134 });
37135 }/*
37136  * Based on:
37137  * Ext JS Library 1.1.1
37138  * Copyright(c) 2006-2007, Ext JS, LLC.
37139  *
37140  * Originally Released Under LGPL - original licence link has changed is not relivant.
37141  *
37142  * Fork - LGPL
37143  * <script type="text/javascript">
37144  */
37145 /**
37146  * @class Roo.tree.TreeEditor
37147  * @extends Roo.Editor
37148  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37149  * as the editor field.
37150  * @constructor
37151  * @param {Object} config (used to be the tree panel.)
37152  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37153  * 
37154  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37155  * @cfg {Roo.form.TextField} field [required] The field configuration
37156  *
37157  * 
37158  */
37159 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37160     var tree = config;
37161     var field;
37162     if (oldconfig) { // old style..
37163         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37164     } else {
37165         // new style..
37166         tree = config.tree;
37167         config.field = config.field  || {};
37168         config.field.xtype = 'TextField';
37169         field = Roo.factory(config.field, Roo.form);
37170     }
37171     config = config || {};
37172     
37173     
37174     this.addEvents({
37175         /**
37176          * @event beforenodeedit
37177          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37178          * false from the handler of this event.
37179          * @param {Editor} this
37180          * @param {Roo.tree.Node} node 
37181          */
37182         "beforenodeedit" : true
37183     });
37184     
37185     //Roo.log(config);
37186     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37187
37188     this.tree = tree;
37189
37190     tree.on('beforeclick', this.beforeNodeClick, this);
37191     tree.getTreeEl().on('mousedown', this.hide, this);
37192     this.on('complete', this.updateNode, this);
37193     this.on('beforestartedit', this.fitToTree, this);
37194     this.on('startedit', this.bindScroll, this, {delay:10});
37195     this.on('specialkey', this.onSpecialKey, this);
37196 };
37197
37198 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37199     /**
37200      * @cfg {String} alignment
37201      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37202      */
37203     alignment: "l-l",
37204     // inherit
37205     autoSize: false,
37206     /**
37207      * @cfg {Boolean} hideEl
37208      * True to hide the bound element while the editor is displayed (defaults to false)
37209      */
37210     hideEl : false,
37211     /**
37212      * @cfg {String} cls
37213      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37214      */
37215     cls: "x-small-editor x-tree-editor",
37216     /**
37217      * @cfg {Boolean} shim
37218      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37219      */
37220     shim:false,
37221     // inherit
37222     shadow:"frame",
37223     /**
37224      * @cfg {Number} maxWidth
37225      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37226      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37227      * scroll and client offsets into account prior to each edit.
37228      */
37229     maxWidth: 250,
37230
37231     editDelay : 350,
37232
37233     // private
37234     fitToTree : function(ed, el){
37235         var td = this.tree.getTreeEl().dom, nd = el.dom;
37236         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37237             td.scrollLeft = nd.offsetLeft;
37238         }
37239         var w = Math.min(
37240                 this.maxWidth,
37241                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37242         this.setSize(w, '');
37243         
37244         return this.fireEvent('beforenodeedit', this, this.editNode);
37245         
37246     },
37247
37248     // private
37249     triggerEdit : function(node){
37250         this.completeEdit();
37251         this.editNode = node;
37252         this.startEdit(node.ui.textNode, node.text);
37253     },
37254
37255     // private
37256     bindScroll : function(){
37257         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37258     },
37259
37260     // private
37261     beforeNodeClick : function(node, e){
37262         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37263         this.lastClick = new Date();
37264         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37265             e.stopEvent();
37266             this.triggerEdit(node);
37267             return false;
37268         }
37269         return true;
37270     },
37271
37272     // private
37273     updateNode : function(ed, value){
37274         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37275         this.editNode.setText(value);
37276     },
37277
37278     // private
37279     onHide : function(){
37280         Roo.tree.TreeEditor.superclass.onHide.call(this);
37281         if(this.editNode){
37282             this.editNode.ui.focus();
37283         }
37284     },
37285
37286     // private
37287     onSpecialKey : function(field, e){
37288         var k = e.getKey();
37289         if(k == e.ESC){
37290             e.stopEvent();
37291             this.cancelEdit();
37292         }else if(k == e.ENTER && !e.hasModifier()){
37293             e.stopEvent();
37294             this.completeEdit();
37295         }
37296     }
37297 });//<Script type="text/javascript">
37298 /*
37299  * Based on:
37300  * Ext JS Library 1.1.1
37301  * Copyright(c) 2006-2007, Ext JS, LLC.
37302  *
37303  * Originally Released Under LGPL - original licence link has changed is not relivant.
37304  *
37305  * Fork - LGPL
37306  * <script type="text/javascript">
37307  */
37308  
37309 /**
37310  * Not documented??? - probably should be...
37311  */
37312
37313 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37314     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37315     
37316     renderElements : function(n, a, targetNode, bulkRender){
37317         //consel.log("renderElements?");
37318         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37319
37320         var t = n.getOwnerTree();
37321         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37322         
37323         var cols = t.columns;
37324         var bw = t.borderWidth;
37325         var c = cols[0];
37326         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37327          var cb = typeof a.checked == "boolean";
37328         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37329         var colcls = 'x-t-' + tid + '-c0';
37330         var buf = [
37331             '<li class="x-tree-node">',
37332             
37333                 
37334                 '<div class="x-tree-node-el ', a.cls,'">',
37335                     // extran...
37336                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37337                 
37338                 
37339                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37340                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37341                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37342                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37343                            (a.iconCls ? ' '+a.iconCls : ''),
37344                            '" unselectable="on" />',
37345                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37346                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37347                              
37348                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37349                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37350                             '<span unselectable="on" qtip="' + tx + '">',
37351                              tx,
37352                              '</span></a>' ,
37353                     '</div>',
37354                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37355                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37356                  ];
37357         for(var i = 1, len = cols.length; i < len; i++){
37358             c = cols[i];
37359             colcls = 'x-t-' + tid + '-c' +i;
37360             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37361             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37362                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37363                       "</div>");
37364          }
37365          
37366          buf.push(
37367             '</a>',
37368             '<div class="x-clear"></div></div>',
37369             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37370             "</li>");
37371         
37372         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37373             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37374                                 n.nextSibling.ui.getEl(), buf.join(""));
37375         }else{
37376             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37377         }
37378         var el = this.wrap.firstChild;
37379         this.elRow = el;
37380         this.elNode = el.firstChild;
37381         this.ranchor = el.childNodes[1];
37382         this.ctNode = this.wrap.childNodes[1];
37383         var cs = el.firstChild.childNodes;
37384         this.indentNode = cs[0];
37385         this.ecNode = cs[1];
37386         this.iconNode = cs[2];
37387         var index = 3;
37388         if(cb){
37389             this.checkbox = cs[3];
37390             index++;
37391         }
37392         this.anchor = cs[index];
37393         
37394         this.textNode = cs[index].firstChild;
37395         
37396         //el.on("click", this.onClick, this);
37397         //el.on("dblclick", this.onDblClick, this);
37398         
37399         
37400        // console.log(this);
37401     },
37402     initEvents : function(){
37403         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37404         
37405             
37406         var a = this.ranchor;
37407
37408         var el = Roo.get(a);
37409
37410         if(Roo.isOpera){ // opera render bug ignores the CSS
37411             el.setStyle("text-decoration", "none");
37412         }
37413
37414         el.on("click", this.onClick, this);
37415         el.on("dblclick", this.onDblClick, this);
37416         el.on("contextmenu", this.onContextMenu, this);
37417         
37418     },
37419     
37420     /*onSelectedChange : function(state){
37421         if(state){
37422             this.focus();
37423             this.addClass("x-tree-selected");
37424         }else{
37425             //this.blur();
37426             this.removeClass("x-tree-selected");
37427         }
37428     },*/
37429     addClass : function(cls){
37430         if(this.elRow){
37431             Roo.fly(this.elRow).addClass(cls);
37432         }
37433         
37434     },
37435     
37436     
37437     removeClass : function(cls){
37438         if(this.elRow){
37439             Roo.fly(this.elRow).removeClass(cls);
37440         }
37441     }
37442
37443     
37444     
37445 });//<Script type="text/javascript">
37446
37447 /*
37448  * Based on:
37449  * Ext JS Library 1.1.1
37450  * Copyright(c) 2006-2007, Ext JS, LLC.
37451  *
37452  * Originally Released Under LGPL - original licence link has changed is not relivant.
37453  *
37454  * Fork - LGPL
37455  * <script type="text/javascript">
37456  */
37457  
37458
37459 /**
37460  * @class Roo.tree.ColumnTree
37461  * @extends Roo.tree.TreePanel
37462  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37463  * @cfg {int} borderWidth  compined right/left border allowance
37464  * @constructor
37465  * @param {String/HTMLElement/Element} el The container element
37466  * @param {Object} config
37467  */
37468 Roo.tree.ColumnTree =  function(el, config)
37469 {
37470    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37471    this.addEvents({
37472         /**
37473         * @event resize
37474         * Fire this event on a container when it resizes
37475         * @param {int} w Width
37476         * @param {int} h Height
37477         */
37478        "resize" : true
37479     });
37480     this.on('resize', this.onResize, this);
37481 };
37482
37483 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37484     //lines:false,
37485     
37486     
37487     borderWidth: Roo.isBorderBox ? 0 : 2, 
37488     headEls : false,
37489     
37490     render : function(){
37491         // add the header.....
37492        
37493         Roo.tree.ColumnTree.superclass.render.apply(this);
37494         
37495         this.el.addClass('x-column-tree');
37496         
37497         this.headers = this.el.createChild(
37498             {cls:'x-tree-headers'},this.innerCt.dom);
37499    
37500         var cols = this.columns, c;
37501         var totalWidth = 0;
37502         this.headEls = [];
37503         var  len = cols.length;
37504         for(var i = 0; i < len; i++){
37505              c = cols[i];
37506              totalWidth += c.width;
37507             this.headEls.push(this.headers.createChild({
37508                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37509                  cn: {
37510                      cls:'x-tree-hd-text',
37511                      html: c.header
37512                  },
37513                  style:'width:'+(c.width-this.borderWidth)+'px;'
37514              }));
37515         }
37516         this.headers.createChild({cls:'x-clear'});
37517         // prevent floats from wrapping when clipped
37518         this.headers.setWidth(totalWidth);
37519         //this.innerCt.setWidth(totalWidth);
37520         this.innerCt.setStyle({ overflow: 'auto' });
37521         this.onResize(this.width, this.height);
37522              
37523         
37524     },
37525     onResize : function(w,h)
37526     {
37527         this.height = h;
37528         this.width = w;
37529         // resize cols..
37530         this.innerCt.setWidth(this.width);
37531         this.innerCt.setHeight(this.height-20);
37532         
37533         // headers...
37534         var cols = this.columns, c;
37535         var totalWidth = 0;
37536         var expEl = false;
37537         var len = cols.length;
37538         for(var i = 0; i < len; i++){
37539             c = cols[i];
37540             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37541                 // it's the expander..
37542                 expEl  = this.headEls[i];
37543                 continue;
37544             }
37545             totalWidth += c.width;
37546             
37547         }
37548         if (expEl) {
37549             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37550         }
37551         this.headers.setWidth(w-20);
37552
37553         
37554         
37555         
37556     }
37557 });
37558 /*
37559  * Based on:
37560  * Ext JS Library 1.1.1
37561  * Copyright(c) 2006-2007, Ext JS, LLC.
37562  *
37563  * Originally Released Under LGPL - original licence link has changed is not relivant.
37564  *
37565  * Fork - LGPL
37566  * <script type="text/javascript">
37567  */
37568  
37569 /**
37570  * @class Roo.menu.Menu
37571  * @extends Roo.util.Observable
37572  * @children Roo.menu.BaseItem
37573  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37574  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37575  * @constructor
37576  * Creates a new Menu
37577  * @param {Object} config Configuration options
37578  */
37579 Roo.menu.Menu = function(config){
37580     
37581     Roo.menu.Menu.superclass.constructor.call(this, config);
37582     
37583     this.id = this.id || Roo.id();
37584     this.addEvents({
37585         /**
37586          * @event beforeshow
37587          * Fires before this menu is displayed
37588          * @param {Roo.menu.Menu} this
37589          */
37590         beforeshow : true,
37591         /**
37592          * @event beforehide
37593          * Fires before this menu is hidden
37594          * @param {Roo.menu.Menu} this
37595          */
37596         beforehide : true,
37597         /**
37598          * @event show
37599          * Fires after this menu is displayed
37600          * @param {Roo.menu.Menu} this
37601          */
37602         show : true,
37603         /**
37604          * @event hide
37605          * Fires after this menu is hidden
37606          * @param {Roo.menu.Menu} this
37607          */
37608         hide : true,
37609         /**
37610          * @event click
37611          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37612          * @param {Roo.menu.Menu} this
37613          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37614          * @param {Roo.EventObject} e
37615          */
37616         click : true,
37617         /**
37618          * @event mouseover
37619          * Fires when the mouse is hovering over this menu
37620          * @param {Roo.menu.Menu} this
37621          * @param {Roo.EventObject} e
37622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37623          */
37624         mouseover : true,
37625         /**
37626          * @event mouseout
37627          * Fires when the mouse exits this menu
37628          * @param {Roo.menu.Menu} this
37629          * @param {Roo.EventObject} e
37630          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37631          */
37632         mouseout : true,
37633         /**
37634          * @event itemclick
37635          * Fires when a menu item contained in this menu is clicked
37636          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37637          * @param {Roo.EventObject} e
37638          */
37639         itemclick: true
37640     });
37641     if (this.registerMenu) {
37642         Roo.menu.MenuMgr.register(this);
37643     }
37644     
37645     var mis = this.items;
37646     this.items = new Roo.util.MixedCollection();
37647     if(mis){
37648         this.add.apply(this, mis);
37649     }
37650 };
37651
37652 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37653     /**
37654      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37655      */
37656     minWidth : 120,
37657     /**
37658      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37659      * for bottom-right shadow (defaults to "sides")
37660      */
37661     shadow : "sides",
37662     /**
37663      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37664      * this menu (defaults to "tl-tr?")
37665      */
37666     subMenuAlign : "tl-tr?",
37667     /**
37668      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37669      * relative to its element of origin (defaults to "tl-bl?")
37670      */
37671     defaultAlign : "tl-bl?",
37672     /**
37673      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37674      */
37675     allowOtherMenus : false,
37676     /**
37677      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37678      */
37679     registerMenu : true,
37680
37681     hidden:true,
37682
37683     // private
37684     render : function(){
37685         if(this.el){
37686             return;
37687         }
37688         var el = this.el = new Roo.Layer({
37689             cls: "x-menu",
37690             shadow:this.shadow,
37691             constrain: false,
37692             parentEl: this.parentEl || document.body,
37693             zindex:15000
37694         });
37695
37696         this.keyNav = new Roo.menu.MenuNav(this);
37697
37698         if(this.plain){
37699             el.addClass("x-menu-plain");
37700         }
37701         if(this.cls){
37702             el.addClass(this.cls);
37703         }
37704         // generic focus element
37705         this.focusEl = el.createChild({
37706             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37707         });
37708         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37709         //disabling touch- as it's causing issues ..
37710         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37711         ul.on('click'   , this.onClick, this);
37712         
37713         
37714         ul.on("mouseover", this.onMouseOver, this);
37715         ul.on("mouseout", this.onMouseOut, this);
37716         this.items.each(function(item){
37717             if (item.hidden) {
37718                 return;
37719             }
37720             
37721             var li = document.createElement("li");
37722             li.className = "x-menu-list-item";
37723             ul.dom.appendChild(li);
37724             item.render(li, this);
37725         }, this);
37726         this.ul = ul;
37727         this.autoWidth();
37728     },
37729
37730     // private
37731     autoWidth : function(){
37732         var el = this.el, ul = this.ul;
37733         if(!el){
37734             return;
37735         }
37736         var w = this.width;
37737         if(w){
37738             el.setWidth(w);
37739         }else if(Roo.isIE){
37740             el.setWidth(this.minWidth);
37741             var t = el.dom.offsetWidth; // force recalc
37742             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37743         }
37744     },
37745
37746     // private
37747     delayAutoWidth : function(){
37748         if(this.rendered){
37749             if(!this.awTask){
37750                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37751             }
37752             this.awTask.delay(20);
37753         }
37754     },
37755
37756     // private
37757     findTargetItem : function(e){
37758         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37759         if(t && t.menuItemId){
37760             return this.items.get(t.menuItemId);
37761         }
37762     },
37763
37764     // private
37765     onClick : function(e){
37766         Roo.log("menu.onClick");
37767         var t = this.findTargetItem(e);
37768         if(!t){
37769             return;
37770         }
37771         Roo.log(e);
37772         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37773             if(t == this.activeItem && t.shouldDeactivate(e)){
37774                 this.activeItem.deactivate();
37775                 delete this.activeItem;
37776                 return;
37777             }
37778             if(t.canActivate){
37779                 this.setActiveItem(t, true);
37780             }
37781             return;
37782             
37783             
37784         }
37785         
37786         t.onClick(e);
37787         this.fireEvent("click", this, t, e);
37788     },
37789
37790     // private
37791     setActiveItem : function(item, autoExpand){
37792         if(item != this.activeItem){
37793             if(this.activeItem){
37794                 this.activeItem.deactivate();
37795             }
37796             this.activeItem = item;
37797             item.activate(autoExpand);
37798         }else if(autoExpand){
37799             item.expandMenu();
37800         }
37801     },
37802
37803     // private
37804     tryActivate : function(start, step){
37805         var items = this.items;
37806         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37807             var item = items.get(i);
37808             if(!item.disabled && item.canActivate){
37809                 this.setActiveItem(item, false);
37810                 return item;
37811             }
37812         }
37813         return false;
37814     },
37815
37816     // private
37817     onMouseOver : function(e){
37818         var t;
37819         if(t = this.findTargetItem(e)){
37820             if(t.canActivate && !t.disabled){
37821                 this.setActiveItem(t, true);
37822             }
37823         }
37824         this.fireEvent("mouseover", this, e, t);
37825     },
37826
37827     // private
37828     onMouseOut : function(e){
37829         var t;
37830         if(t = this.findTargetItem(e)){
37831             if(t == this.activeItem && t.shouldDeactivate(e)){
37832                 this.activeItem.deactivate();
37833                 delete this.activeItem;
37834             }
37835         }
37836         this.fireEvent("mouseout", this, e, t);
37837     },
37838
37839     /**
37840      * Read-only.  Returns true if the menu is currently displayed, else false.
37841      * @type Boolean
37842      */
37843     isVisible : function(){
37844         return this.el && !this.hidden;
37845     },
37846
37847     /**
37848      * Displays this menu relative to another element
37849      * @param {String/HTMLElement/Roo.Element} element The element to align to
37850      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37851      * the element (defaults to this.defaultAlign)
37852      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37853      */
37854     show : function(el, pos, parentMenu){
37855         this.parentMenu = parentMenu;
37856         if(!this.el){
37857             this.render();
37858         }
37859         this.fireEvent("beforeshow", this);
37860         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37861     },
37862
37863     /**
37864      * Displays this menu at a specific xy position
37865      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37866      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37867      */
37868     showAt : function(xy, parentMenu, /* private: */_e){
37869         this.parentMenu = parentMenu;
37870         if(!this.el){
37871             this.render();
37872         }
37873         if(_e !== false){
37874             this.fireEvent("beforeshow", this);
37875             xy = this.el.adjustForConstraints(xy);
37876         }
37877         this.el.setXY(xy);
37878         this.el.show();
37879         this.hidden = false;
37880         this.focus();
37881         this.fireEvent("show", this);
37882     },
37883
37884     focus : function(){
37885         if(!this.hidden){
37886             this.doFocus.defer(50, this);
37887         }
37888     },
37889
37890     doFocus : function(){
37891         if(!this.hidden){
37892             this.focusEl.focus();
37893         }
37894     },
37895
37896     /**
37897      * Hides this menu and optionally all parent menus
37898      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37899      */
37900     hide : function(deep){
37901         if(this.el && this.isVisible()){
37902             this.fireEvent("beforehide", this);
37903             if(this.activeItem){
37904                 this.activeItem.deactivate();
37905                 this.activeItem = null;
37906             }
37907             this.el.hide();
37908             this.hidden = true;
37909             this.fireEvent("hide", this);
37910         }
37911         if(deep === true && this.parentMenu){
37912             this.parentMenu.hide(true);
37913         }
37914     },
37915
37916     /**
37917      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37918      * Any of the following are valid:
37919      * <ul>
37920      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37921      * <li>An HTMLElement object which will be converted to a menu item</li>
37922      * <li>A menu item config object that will be created as a new menu item</li>
37923      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37924      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37925      * </ul>
37926      * Usage:
37927      * <pre><code>
37928 // Create the menu
37929 var menu = new Roo.menu.Menu();
37930
37931 // Create a menu item to add by reference
37932 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37933
37934 // Add a bunch of items at once using different methods.
37935 // Only the last item added will be returned.
37936 var item = menu.add(
37937     menuItem,                // add existing item by ref
37938     'Dynamic Item',          // new TextItem
37939     '-',                     // new separator
37940     { text: 'Config Item' }  // new item by config
37941 );
37942 </code></pre>
37943      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37944      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37945      */
37946     add : function(){
37947         var a = arguments, l = a.length, item;
37948         for(var i = 0; i < l; i++){
37949             var el = a[i];
37950             if ((typeof(el) == "object") && el.xtype && el.xns) {
37951                 el = Roo.factory(el, Roo.menu);
37952             }
37953             
37954             if(el.render){ // some kind of Item
37955                 item = this.addItem(el);
37956             }else if(typeof el == "string"){ // string
37957                 if(el == "separator" || el == "-"){
37958                     item = this.addSeparator();
37959                 }else{
37960                     item = this.addText(el);
37961                 }
37962             }else if(el.tagName || el.el){ // element
37963                 item = this.addElement(el);
37964             }else if(typeof el == "object"){ // must be menu item config?
37965                 item = this.addMenuItem(el);
37966             }
37967         }
37968         return item;
37969     },
37970
37971     /**
37972      * Returns this menu's underlying {@link Roo.Element} object
37973      * @return {Roo.Element} The element
37974      */
37975     getEl : function(){
37976         if(!this.el){
37977             this.render();
37978         }
37979         return this.el;
37980     },
37981
37982     /**
37983      * Adds a separator bar to the menu
37984      * @return {Roo.menu.Item} The menu item that was added
37985      */
37986     addSeparator : function(){
37987         return this.addItem(new Roo.menu.Separator());
37988     },
37989
37990     /**
37991      * Adds an {@link Roo.Element} object to the menu
37992      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37993      * @return {Roo.menu.Item} The menu item that was added
37994      */
37995     addElement : function(el){
37996         return this.addItem(new Roo.menu.BaseItem(el));
37997     },
37998
37999     /**
38000      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38001      * @param {Roo.menu.Item} item The menu item to add
38002      * @return {Roo.menu.Item} The menu item that was added
38003      */
38004     addItem : function(item){
38005         this.items.add(item);
38006         if(this.ul){
38007             var li = document.createElement("li");
38008             li.className = "x-menu-list-item";
38009             this.ul.dom.appendChild(li);
38010             item.render(li, this);
38011             this.delayAutoWidth();
38012         }
38013         return item;
38014     },
38015
38016     /**
38017      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38018      * @param {Object} config A MenuItem config object
38019      * @return {Roo.menu.Item} The menu item that was added
38020      */
38021     addMenuItem : function(config){
38022         if(!(config instanceof Roo.menu.Item)){
38023             if(typeof config.checked == "boolean"){ // must be check menu item config?
38024                 config = new Roo.menu.CheckItem(config);
38025             }else{
38026                 config = new Roo.menu.Item(config);
38027             }
38028         }
38029         return this.addItem(config);
38030     },
38031
38032     /**
38033      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38034      * @param {String} text The text to display in the menu item
38035      * @return {Roo.menu.Item} The menu item that was added
38036      */
38037     addText : function(text){
38038         return this.addItem(new Roo.menu.TextItem({ text : text }));
38039     },
38040
38041     /**
38042      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38043      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38044      * @param {Roo.menu.Item} item The menu item to add
38045      * @return {Roo.menu.Item} The menu item that was added
38046      */
38047     insert : function(index, item){
38048         this.items.insert(index, item);
38049         if(this.ul){
38050             var li = document.createElement("li");
38051             li.className = "x-menu-list-item";
38052             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38053             item.render(li, this);
38054             this.delayAutoWidth();
38055         }
38056         return item;
38057     },
38058
38059     /**
38060      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38061      * @param {Roo.menu.Item} item The menu item to remove
38062      */
38063     remove : function(item){
38064         this.items.removeKey(item.id);
38065         item.destroy();
38066     },
38067
38068     /**
38069      * Removes and destroys all items in the menu
38070      */
38071     removeAll : function(){
38072         var f;
38073         while(f = this.items.first()){
38074             this.remove(f);
38075         }
38076     }
38077 });
38078
38079 // MenuNav is a private utility class used internally by the Menu
38080 Roo.menu.MenuNav = function(menu){
38081     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38082     this.scope = this.menu = menu;
38083 };
38084
38085 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38086     doRelay : function(e, h){
38087         var k = e.getKey();
38088         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38089             this.menu.tryActivate(0, 1);
38090             return false;
38091         }
38092         return h.call(this.scope || this, e, this.menu);
38093     },
38094
38095     up : function(e, m){
38096         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38097             m.tryActivate(m.items.length-1, -1);
38098         }
38099     },
38100
38101     down : function(e, m){
38102         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38103             m.tryActivate(0, 1);
38104         }
38105     },
38106
38107     right : function(e, m){
38108         if(m.activeItem){
38109             m.activeItem.expandMenu(true);
38110         }
38111     },
38112
38113     left : function(e, m){
38114         m.hide();
38115         if(m.parentMenu && m.parentMenu.activeItem){
38116             m.parentMenu.activeItem.activate();
38117         }
38118     },
38119
38120     enter : function(e, m){
38121         if(m.activeItem){
38122             e.stopPropagation();
38123             m.activeItem.onClick(e);
38124             m.fireEvent("click", this, m.activeItem);
38125             return true;
38126         }
38127     }
38128 });/*
38129  * Based on:
38130  * Ext JS Library 1.1.1
38131  * Copyright(c) 2006-2007, Ext JS, LLC.
38132  *
38133  * Originally Released Under LGPL - original licence link has changed is not relivant.
38134  *
38135  * Fork - LGPL
38136  * <script type="text/javascript">
38137  */
38138  
38139 /**
38140  * @class Roo.menu.MenuMgr
38141  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38142  * @static
38143  */
38144 Roo.menu.MenuMgr = function(){
38145    var menus, active, groups = {}, attached = false, lastShow = new Date();
38146
38147    // private - called when first menu is created
38148    function init(){
38149        menus = {};
38150        active = new Roo.util.MixedCollection();
38151        Roo.get(document).addKeyListener(27, function(){
38152            if(active.length > 0){
38153                hideAll();
38154            }
38155        });
38156    }
38157
38158    // private
38159    function hideAll(){
38160        if(active && active.length > 0){
38161            var c = active.clone();
38162            c.each(function(m){
38163                m.hide();
38164            });
38165        }
38166    }
38167
38168    // private
38169    function onHide(m){
38170        active.remove(m);
38171        if(active.length < 1){
38172            Roo.get(document).un("mousedown", onMouseDown);
38173            attached = false;
38174        }
38175    }
38176
38177    // private
38178    function onShow(m){
38179        var last = active.last();
38180        lastShow = new Date();
38181        active.add(m);
38182        if(!attached){
38183            Roo.get(document).on("mousedown", onMouseDown);
38184            attached = true;
38185        }
38186        if(m.parentMenu){
38187           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38188           m.parentMenu.activeChild = m;
38189        }else if(last && last.isVisible()){
38190           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38191        }
38192    }
38193
38194    // private
38195    function onBeforeHide(m){
38196        if(m.activeChild){
38197            m.activeChild.hide();
38198        }
38199        if(m.autoHideTimer){
38200            clearTimeout(m.autoHideTimer);
38201            delete m.autoHideTimer;
38202        }
38203    }
38204
38205    // private
38206    function onBeforeShow(m){
38207        var pm = m.parentMenu;
38208        if(!pm && !m.allowOtherMenus){
38209            hideAll();
38210        }else if(pm && pm.activeChild && active != m){
38211            pm.activeChild.hide();
38212        }
38213    }
38214
38215    // private
38216    function onMouseDown(e){
38217        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38218            hideAll();
38219        }
38220    }
38221
38222    // private
38223    function onBeforeCheck(mi, state){
38224        if(state){
38225            var g = groups[mi.group];
38226            for(var i = 0, l = g.length; i < l; i++){
38227                if(g[i] != mi){
38228                    g[i].setChecked(false);
38229                }
38230            }
38231        }
38232    }
38233
38234    return {
38235
38236        /**
38237         * Hides all menus that are currently visible
38238         */
38239        hideAll : function(){
38240             hideAll();  
38241        },
38242
38243        // private
38244        register : function(menu){
38245            if(!menus){
38246                init();
38247            }
38248            menus[menu.id] = menu;
38249            menu.on("beforehide", onBeforeHide);
38250            menu.on("hide", onHide);
38251            menu.on("beforeshow", onBeforeShow);
38252            menu.on("show", onShow);
38253            var g = menu.group;
38254            if(g && menu.events["checkchange"]){
38255                if(!groups[g]){
38256                    groups[g] = [];
38257                }
38258                groups[g].push(menu);
38259                menu.on("checkchange", onCheck);
38260            }
38261        },
38262
38263         /**
38264          * Returns a {@link Roo.menu.Menu} object
38265          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38266          * be used to generate and return a new Menu instance.
38267          */
38268        get : function(menu){
38269            if(typeof menu == "string"){ // menu id
38270                return menus[menu];
38271            }else if(menu.events){  // menu instance
38272                return menu;
38273            }else if(typeof menu.length == 'number'){ // array of menu items?
38274                return new Roo.menu.Menu({items:menu});
38275            }else{ // otherwise, must be a config
38276                return new Roo.menu.Menu(menu);
38277            }
38278        },
38279
38280        // private
38281        unregister : function(menu){
38282            delete menus[menu.id];
38283            menu.un("beforehide", onBeforeHide);
38284            menu.un("hide", onHide);
38285            menu.un("beforeshow", onBeforeShow);
38286            menu.un("show", onShow);
38287            var g = menu.group;
38288            if(g && menu.events["checkchange"]){
38289                groups[g].remove(menu);
38290                menu.un("checkchange", onCheck);
38291            }
38292        },
38293
38294        // private
38295        registerCheckable : function(menuItem){
38296            var g = menuItem.group;
38297            if(g){
38298                if(!groups[g]){
38299                    groups[g] = [];
38300                }
38301                groups[g].push(menuItem);
38302                menuItem.on("beforecheckchange", onBeforeCheck);
38303            }
38304        },
38305
38306        // private
38307        unregisterCheckable : function(menuItem){
38308            var g = menuItem.group;
38309            if(g){
38310                groups[g].remove(menuItem);
38311                menuItem.un("beforecheckchange", onBeforeCheck);
38312            }
38313        }
38314    };
38315 }();/*
38316  * Based on:
38317  * Ext JS Library 1.1.1
38318  * Copyright(c) 2006-2007, Ext JS, LLC.
38319  *
38320  * Originally Released Under LGPL - original licence link has changed is not relivant.
38321  *
38322  * Fork - LGPL
38323  * <script type="text/javascript">
38324  */
38325  
38326
38327 /**
38328  * @class Roo.menu.BaseItem
38329  * @extends Roo.Component
38330  * @abstract
38331  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38332  * management and base configuration options shared by all menu components.
38333  * @constructor
38334  * Creates a new BaseItem
38335  * @param {Object} config Configuration options
38336  */
38337 Roo.menu.BaseItem = function(config){
38338     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38339
38340     this.addEvents({
38341         /**
38342          * @event click
38343          * Fires when this item is clicked
38344          * @param {Roo.menu.BaseItem} this
38345          * @param {Roo.EventObject} e
38346          */
38347         click: true,
38348         /**
38349          * @event activate
38350          * Fires when this item is activated
38351          * @param {Roo.menu.BaseItem} this
38352          */
38353         activate : true,
38354         /**
38355          * @event deactivate
38356          * Fires when this item is deactivated
38357          * @param {Roo.menu.BaseItem} this
38358          */
38359         deactivate : true
38360     });
38361
38362     if(this.handler){
38363         this.on("click", this.handler, this.scope, true);
38364     }
38365 };
38366
38367 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38368     /**
38369      * @cfg {Function} handler
38370      * A function that will handle the click event of this menu item (defaults to undefined)
38371      */
38372     /**
38373      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38374      */
38375     canActivate : false,
38376     
38377      /**
38378      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38379      */
38380     hidden: false,
38381     
38382     /**
38383      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38384      */
38385     activeClass : "x-menu-item-active",
38386     /**
38387      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38388      */
38389     hideOnClick : true,
38390     /**
38391      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38392      */
38393     hideDelay : 100,
38394
38395     // private
38396     ctype: "Roo.menu.BaseItem",
38397
38398     // private
38399     actionMode : "container",
38400
38401     // private
38402     render : function(container, parentMenu){
38403         this.parentMenu = parentMenu;
38404         Roo.menu.BaseItem.superclass.render.call(this, container);
38405         this.container.menuItemId = this.id;
38406     },
38407
38408     // private
38409     onRender : function(container, position){
38410         this.el = Roo.get(this.el);
38411         container.dom.appendChild(this.el.dom);
38412     },
38413
38414     // private
38415     onClick : function(e){
38416         if(!this.disabled && this.fireEvent("click", this, e) !== false
38417                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38418             this.handleClick(e);
38419         }else{
38420             e.stopEvent();
38421         }
38422     },
38423
38424     // private
38425     activate : function(){
38426         if(this.disabled){
38427             return false;
38428         }
38429         var li = this.container;
38430         li.addClass(this.activeClass);
38431         this.region = li.getRegion().adjust(2, 2, -2, -2);
38432         this.fireEvent("activate", this);
38433         return true;
38434     },
38435
38436     // private
38437     deactivate : function(){
38438         this.container.removeClass(this.activeClass);
38439         this.fireEvent("deactivate", this);
38440     },
38441
38442     // private
38443     shouldDeactivate : function(e){
38444         return !this.region || !this.region.contains(e.getPoint());
38445     },
38446
38447     // private
38448     handleClick : function(e){
38449         if(this.hideOnClick){
38450             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38451         }
38452     },
38453
38454     // private
38455     expandMenu : function(autoActivate){
38456         // do nothing
38457     },
38458
38459     // private
38460     hideMenu : function(){
38461         // do nothing
38462     }
38463 });/*
38464  * Based on:
38465  * Ext JS Library 1.1.1
38466  * Copyright(c) 2006-2007, Ext JS, LLC.
38467  *
38468  * Originally Released Under LGPL - original licence link has changed is not relivant.
38469  *
38470  * Fork - LGPL
38471  * <script type="text/javascript">
38472  */
38473  
38474 /**
38475  * @class Roo.menu.Adapter
38476  * @extends Roo.menu.BaseItem
38477  * @abstract
38478  * 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.
38479  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38480  * @constructor
38481  * Creates a new Adapter
38482  * @param {Object} config Configuration options
38483  */
38484 Roo.menu.Adapter = function(component, config){
38485     Roo.menu.Adapter.superclass.constructor.call(this, config);
38486     this.component = component;
38487 };
38488 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38489     // private
38490     canActivate : true,
38491
38492     // private
38493     onRender : function(container, position){
38494         this.component.render(container);
38495         this.el = this.component.getEl();
38496     },
38497
38498     // private
38499     activate : function(){
38500         if(this.disabled){
38501             return false;
38502         }
38503         this.component.focus();
38504         this.fireEvent("activate", this);
38505         return true;
38506     },
38507
38508     // private
38509     deactivate : function(){
38510         this.fireEvent("deactivate", this);
38511     },
38512
38513     // private
38514     disable : function(){
38515         this.component.disable();
38516         Roo.menu.Adapter.superclass.disable.call(this);
38517     },
38518
38519     // private
38520     enable : function(){
38521         this.component.enable();
38522         Roo.menu.Adapter.superclass.enable.call(this);
38523     }
38524 });/*
38525  * Based on:
38526  * Ext JS Library 1.1.1
38527  * Copyright(c) 2006-2007, Ext JS, LLC.
38528  *
38529  * Originally Released Under LGPL - original licence link has changed is not relivant.
38530  *
38531  * Fork - LGPL
38532  * <script type="text/javascript">
38533  */
38534
38535 /**
38536  * @class Roo.menu.TextItem
38537  * @extends Roo.menu.BaseItem
38538  * Adds a static text string to a menu, usually used as either a heading or group separator.
38539  * Note: old style constructor with text is still supported.
38540  * 
38541  * @constructor
38542  * Creates a new TextItem
38543  * @param {Object} cfg Configuration
38544  */
38545 Roo.menu.TextItem = function(cfg){
38546     if (typeof(cfg) == 'string') {
38547         this.text = cfg;
38548     } else {
38549         Roo.apply(this,cfg);
38550     }
38551     
38552     Roo.menu.TextItem.superclass.constructor.call(this);
38553 };
38554
38555 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38556     /**
38557      * @cfg {String} text Text to show on item.
38558      */
38559     text : '',
38560     
38561     /**
38562      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38563      */
38564     hideOnClick : false,
38565     /**
38566      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38567      */
38568     itemCls : "x-menu-text",
38569
38570     // private
38571     onRender : function(){
38572         var s = document.createElement("span");
38573         s.className = this.itemCls;
38574         s.innerHTML = this.text;
38575         this.el = s;
38576         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38577     }
38578 });/*
38579  * Based on:
38580  * Ext JS Library 1.1.1
38581  * Copyright(c) 2006-2007, Ext JS, LLC.
38582  *
38583  * Originally Released Under LGPL - original licence link has changed is not relivant.
38584  *
38585  * Fork - LGPL
38586  * <script type="text/javascript">
38587  */
38588
38589 /**
38590  * @class Roo.menu.Separator
38591  * @extends Roo.menu.BaseItem
38592  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38593  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38594  * @constructor
38595  * @param {Object} config Configuration options
38596  */
38597 Roo.menu.Separator = function(config){
38598     Roo.menu.Separator.superclass.constructor.call(this, config);
38599 };
38600
38601 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38602     /**
38603      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38604      */
38605     itemCls : "x-menu-sep",
38606     /**
38607      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38608      */
38609     hideOnClick : false,
38610
38611     // private
38612     onRender : function(li){
38613         var s = document.createElement("span");
38614         s.className = this.itemCls;
38615         s.innerHTML = "&#160;";
38616         this.el = s;
38617         li.addClass("x-menu-sep-li");
38618         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38619     }
38620 });/*
38621  * Based on:
38622  * Ext JS Library 1.1.1
38623  * Copyright(c) 2006-2007, Ext JS, LLC.
38624  *
38625  * Originally Released Under LGPL - original licence link has changed is not relivant.
38626  *
38627  * Fork - LGPL
38628  * <script type="text/javascript">
38629  */
38630 /**
38631  * @class Roo.menu.Item
38632  * @extends Roo.menu.BaseItem
38633  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38634  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38635  * activation and click handling.
38636  * @constructor
38637  * Creates a new Item
38638  * @param {Object} config Configuration options
38639  */
38640 Roo.menu.Item = function(config){
38641     Roo.menu.Item.superclass.constructor.call(this, config);
38642     if(this.menu){
38643         this.menu = Roo.menu.MenuMgr.get(this.menu);
38644     }
38645 };
38646 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38647     /**
38648      * @cfg {Roo.menu.Menu} menu
38649      * A Sub menu
38650      */
38651     /**
38652      * @cfg {String} text
38653      * The text to show on the menu item.
38654      */
38655     text: '',
38656      /**
38657      * @cfg {String} HTML to render in menu
38658      * The text to show on the menu item (HTML version).
38659      */
38660     html: '',
38661     /**
38662      * @cfg {String} icon
38663      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38664      */
38665     icon: undefined,
38666     /**
38667      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38668      */
38669     itemCls : "x-menu-item",
38670     /**
38671      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38672      */
38673     canActivate : true,
38674     /**
38675      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38676      */
38677     showDelay: 200,
38678     // doc'd in BaseItem
38679     hideDelay: 200,
38680
38681     // private
38682     ctype: "Roo.menu.Item",
38683     
38684     // private
38685     onRender : function(container, position){
38686         var el = document.createElement("a");
38687         el.hideFocus = true;
38688         el.unselectable = "on";
38689         el.href = this.href || "#";
38690         if(this.hrefTarget){
38691             el.target = this.hrefTarget;
38692         }
38693         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38694         
38695         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38696         
38697         el.innerHTML = String.format(
38698                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38699                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38700         this.el = el;
38701         Roo.menu.Item.superclass.onRender.call(this, container, position);
38702     },
38703
38704     /**
38705      * Sets the text to display in this menu item
38706      * @param {String} text The text to display
38707      * @param {Boolean} isHTML true to indicate text is pure html.
38708      */
38709     setText : function(text, isHTML){
38710         if (isHTML) {
38711             this.html = text;
38712         } else {
38713             this.text = text;
38714             this.html = '';
38715         }
38716         if(this.rendered){
38717             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38718      
38719             this.el.update(String.format(
38720                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38721                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38722             this.parentMenu.autoWidth();
38723         }
38724     },
38725
38726     // private
38727     handleClick : function(e){
38728         if(!this.href){ // if no link defined, stop the event automatically
38729             e.stopEvent();
38730         }
38731         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38732     },
38733
38734     // private
38735     activate : function(autoExpand){
38736         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38737             this.focus();
38738             if(autoExpand){
38739                 this.expandMenu();
38740             }
38741         }
38742         return true;
38743     },
38744
38745     // private
38746     shouldDeactivate : function(e){
38747         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38748             if(this.menu && this.menu.isVisible()){
38749                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38750             }
38751             return true;
38752         }
38753         return false;
38754     },
38755
38756     // private
38757     deactivate : function(){
38758         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38759         this.hideMenu();
38760     },
38761
38762     // private
38763     expandMenu : function(autoActivate){
38764         if(!this.disabled && this.menu){
38765             clearTimeout(this.hideTimer);
38766             delete this.hideTimer;
38767             if(!this.menu.isVisible() && !this.showTimer){
38768                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38769             }else if (this.menu.isVisible() && autoActivate){
38770                 this.menu.tryActivate(0, 1);
38771             }
38772         }
38773     },
38774
38775     // private
38776     deferExpand : function(autoActivate){
38777         delete this.showTimer;
38778         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38779         if(autoActivate){
38780             this.menu.tryActivate(0, 1);
38781         }
38782     },
38783
38784     // private
38785     hideMenu : function(){
38786         clearTimeout(this.showTimer);
38787         delete this.showTimer;
38788         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38789             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38790         }
38791     },
38792
38793     // private
38794     deferHide : function(){
38795         delete this.hideTimer;
38796         this.menu.hide();
38797     }
38798 });/*
38799  * Based on:
38800  * Ext JS Library 1.1.1
38801  * Copyright(c) 2006-2007, Ext JS, LLC.
38802  *
38803  * Originally Released Under LGPL - original licence link has changed is not relivant.
38804  *
38805  * Fork - LGPL
38806  * <script type="text/javascript">
38807  */
38808  
38809 /**
38810  * @class Roo.menu.CheckItem
38811  * @extends Roo.menu.Item
38812  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38813  * @constructor
38814  * Creates a new CheckItem
38815  * @param {Object} config Configuration options
38816  */
38817 Roo.menu.CheckItem = function(config){
38818     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38819     this.addEvents({
38820         /**
38821          * @event beforecheckchange
38822          * Fires before the checked value is set, providing an opportunity to cancel if needed
38823          * @param {Roo.menu.CheckItem} this
38824          * @param {Boolean} checked The new checked value that will be set
38825          */
38826         "beforecheckchange" : true,
38827         /**
38828          * @event checkchange
38829          * Fires after the checked value has been set
38830          * @param {Roo.menu.CheckItem} this
38831          * @param {Boolean} checked The checked value that was set
38832          */
38833         "checkchange" : true
38834     });
38835     if(this.checkHandler){
38836         this.on('checkchange', this.checkHandler, this.scope);
38837     }
38838 };
38839 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38840     /**
38841      * @cfg {String} group
38842      * All check items with the same group name will automatically be grouped into a single-select
38843      * radio button group (defaults to '')
38844      */
38845     /**
38846      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38847      */
38848     itemCls : "x-menu-item x-menu-check-item",
38849     /**
38850      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38851      */
38852     groupClass : "x-menu-group-item",
38853
38854     /**
38855      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38856      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38857      * initialized with checked = true will be rendered as checked.
38858      */
38859     checked: false,
38860
38861     // private
38862     ctype: "Roo.menu.CheckItem",
38863
38864     // private
38865     onRender : function(c){
38866         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38867         if(this.group){
38868             this.el.addClass(this.groupClass);
38869         }
38870         Roo.menu.MenuMgr.registerCheckable(this);
38871         if(this.checked){
38872             this.checked = false;
38873             this.setChecked(true, true);
38874         }
38875     },
38876
38877     // private
38878     destroy : function(){
38879         if(this.rendered){
38880             Roo.menu.MenuMgr.unregisterCheckable(this);
38881         }
38882         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38883     },
38884
38885     /**
38886      * Set the checked state of this item
38887      * @param {Boolean} checked The new checked value
38888      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38889      */
38890     setChecked : function(state, suppressEvent){
38891         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38892             if(this.container){
38893                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38894             }
38895             this.checked = state;
38896             if(suppressEvent !== true){
38897                 this.fireEvent("checkchange", this, state);
38898             }
38899         }
38900     },
38901
38902     // private
38903     handleClick : function(e){
38904        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38905            this.setChecked(!this.checked);
38906        }
38907        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38908     }
38909 });/*
38910  * Based on:
38911  * Ext JS Library 1.1.1
38912  * Copyright(c) 2006-2007, Ext JS, LLC.
38913  *
38914  * Originally Released Under LGPL - original licence link has changed is not relivant.
38915  *
38916  * Fork - LGPL
38917  * <script type="text/javascript">
38918  */
38919  
38920 /**
38921  * @class Roo.menu.DateItem
38922  * @extends Roo.menu.Adapter
38923  * A menu item that wraps the {@link Roo.DatPicker} component.
38924  * @constructor
38925  * Creates a new DateItem
38926  * @param {Object} config Configuration options
38927  */
38928 Roo.menu.DateItem = function(config){
38929     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38930     /** The Roo.DatePicker object @type Roo.DatePicker */
38931     this.picker = this.component;
38932     this.addEvents({select: true});
38933     
38934     this.picker.on("render", function(picker){
38935         picker.getEl().swallowEvent("click");
38936         picker.container.addClass("x-menu-date-item");
38937     });
38938
38939     this.picker.on("select", this.onSelect, this);
38940 };
38941
38942 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38943     // private
38944     onSelect : function(picker, date){
38945         this.fireEvent("select", this, date, picker);
38946         Roo.menu.DateItem.superclass.handleClick.call(this);
38947     }
38948 });/*
38949  * Based on:
38950  * Ext JS Library 1.1.1
38951  * Copyright(c) 2006-2007, Ext JS, LLC.
38952  *
38953  * Originally Released Under LGPL - original licence link has changed is not relivant.
38954  *
38955  * Fork - LGPL
38956  * <script type="text/javascript">
38957  */
38958  
38959 /**
38960  * @class Roo.menu.ColorItem
38961  * @extends Roo.menu.Adapter
38962  * A menu item that wraps the {@link Roo.ColorPalette} component.
38963  * @constructor
38964  * Creates a new ColorItem
38965  * @param {Object} config Configuration options
38966  */
38967 Roo.menu.ColorItem = function(config){
38968     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38969     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38970     this.palette = this.component;
38971     this.relayEvents(this.palette, ["select"]);
38972     if(this.selectHandler){
38973         this.on('select', this.selectHandler, this.scope);
38974     }
38975 };
38976 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38977  * Based on:
38978  * Ext JS Library 1.1.1
38979  * Copyright(c) 2006-2007, Ext JS, LLC.
38980  *
38981  * Originally Released Under LGPL - original licence link has changed is not relivant.
38982  *
38983  * Fork - LGPL
38984  * <script type="text/javascript">
38985  */
38986  
38987
38988 /**
38989  * @class Roo.menu.DateMenu
38990  * @extends Roo.menu.Menu
38991  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38992  * @constructor
38993  * Creates a new DateMenu
38994  * @param {Object} config Configuration options
38995  */
38996 Roo.menu.DateMenu = function(config){
38997     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38998     this.plain = true;
38999     var di = new Roo.menu.DateItem(config);
39000     this.add(di);
39001     /**
39002      * The {@link Roo.DatePicker} instance for this DateMenu
39003      * @type DatePicker
39004      */
39005     this.picker = di.picker;
39006     /**
39007      * @event select
39008      * @param {DatePicker} picker
39009      * @param {Date} date
39010      */
39011     this.relayEvents(di, ["select"]);
39012     this.on('beforeshow', function(){
39013         if(this.picker){
39014             this.picker.hideMonthPicker(false);
39015         }
39016     }, this);
39017 };
39018 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39019     cls:'x-date-menu'
39020 });/*
39021  * Based on:
39022  * Ext JS Library 1.1.1
39023  * Copyright(c) 2006-2007, Ext JS, LLC.
39024  *
39025  * Originally Released Under LGPL - original licence link has changed is not relivant.
39026  *
39027  * Fork - LGPL
39028  * <script type="text/javascript">
39029  */
39030  
39031
39032 /**
39033  * @class Roo.menu.ColorMenu
39034  * @extends Roo.menu.Menu
39035  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39036  * @constructor
39037  * Creates a new ColorMenu
39038  * @param {Object} config Configuration options
39039  */
39040 Roo.menu.ColorMenu = function(config){
39041     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39042     this.plain = true;
39043     var ci = new Roo.menu.ColorItem(config);
39044     this.add(ci);
39045     /**
39046      * The {@link Roo.ColorPalette} instance for this ColorMenu
39047      * @type ColorPalette
39048      */
39049     this.palette = ci.palette;
39050     /**
39051      * @event select
39052      * @param {ColorPalette} palette
39053      * @param {String} color
39054      */
39055     this.relayEvents(ci, ["select"]);
39056 };
39057 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39058  * Based on:
39059  * Ext JS Library 1.1.1
39060  * Copyright(c) 2006-2007, Ext JS, LLC.
39061  *
39062  * Originally Released Under LGPL - original licence link has changed is not relivant.
39063  *
39064  * Fork - LGPL
39065  * <script type="text/javascript">
39066  */
39067  
39068 /**
39069  * @class Roo.form.TextItem
39070  * @extends Roo.BoxComponent
39071  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39072  * @constructor
39073  * Creates a new TextItem
39074  * @param {Object} config Configuration options
39075  */
39076 Roo.form.TextItem = function(config){
39077     Roo.form.TextItem.superclass.constructor.call(this, config);
39078 };
39079
39080 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39081     
39082     /**
39083      * @cfg {String} tag the tag for this item (default div)
39084      */
39085     tag : 'div',
39086     /**
39087      * @cfg {String} html the content for this item
39088      */
39089     html : '',
39090     
39091     getAutoCreate : function()
39092     {
39093         var cfg = {
39094             id: this.id,
39095             tag: this.tag,
39096             html: this.html,
39097             cls: 'x-form-item'
39098         };
39099         
39100         return cfg;
39101         
39102     },
39103     
39104     onRender : function(ct, position)
39105     {
39106         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39107         
39108         if(!this.el){
39109             var cfg = this.getAutoCreate();
39110             if(!cfg.name){
39111                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39112             }
39113             if (!cfg.name.length) {
39114                 delete cfg.name;
39115             }
39116             this.el = ct.createChild(cfg, position);
39117         }
39118     },
39119     /*
39120      * setHTML
39121      * @param {String} html update the Contents of the element.
39122      */
39123     setHTML : function(html)
39124     {
39125         this.fieldEl.dom.innerHTML = html;
39126     }
39127     
39128 });/*
39129  * Based on:
39130  * Ext JS Library 1.1.1
39131  * Copyright(c) 2006-2007, Ext JS, LLC.
39132  *
39133  * Originally Released Under LGPL - original licence link has changed is not relivant.
39134  *
39135  * Fork - LGPL
39136  * <script type="text/javascript">
39137  */
39138  
39139 /**
39140  * @class Roo.form.Field
39141  * @extends Roo.BoxComponent
39142  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39143  * @constructor
39144  * Creates a new Field
39145  * @param {Object} config Configuration options
39146  */
39147 Roo.form.Field = function(config){
39148     Roo.form.Field.superclass.constructor.call(this, config);
39149 };
39150
39151 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39152     /**
39153      * @cfg {String} fieldLabel Label to use when rendering a form.
39154      */
39155        /**
39156      * @cfg {String} qtip Mouse over tip
39157      */
39158      
39159     /**
39160      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39161      */
39162     invalidClass : "x-form-invalid",
39163     /**
39164      * @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")
39165      */
39166     invalidText : "The value in this field is invalid",
39167     /**
39168      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39169      */
39170     focusClass : "x-form-focus",
39171     /**
39172      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39173       automatic validation (defaults to "keyup").
39174      */
39175     validationEvent : "keyup",
39176     /**
39177      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39178      */
39179     validateOnBlur : true,
39180     /**
39181      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39182      */
39183     validationDelay : 250,
39184     /**
39185      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39186      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39187      */
39188     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39189     /**
39190      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39191      */
39192     fieldClass : "x-form-field",
39193     /**
39194      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39195      *<pre>
39196 Value         Description
39197 -----------   ----------------------------------------------------------------------
39198 qtip          Display a quick tip when the user hovers over the field
39199 title         Display a default browser title attribute popup
39200 under         Add a block div beneath the field containing the error text
39201 side          Add an error icon to the right of the field with a popup on hover
39202 [element id]  Add the error text directly to the innerHTML of the specified element
39203 </pre>
39204      */
39205     msgTarget : 'qtip',
39206     /**
39207      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39208      */
39209     msgFx : 'normal',
39210
39211     /**
39212      * @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.
39213      */
39214     readOnly : false,
39215
39216     /**
39217      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39218      */
39219     disabled : false,
39220
39221     /**
39222      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39223      */
39224     inputType : undefined,
39225     
39226     /**
39227      * @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).
39228          */
39229         tabIndex : undefined,
39230         
39231     // private
39232     isFormField : true,
39233
39234     // private
39235     hasFocus : false,
39236     /**
39237      * @property {Roo.Element} fieldEl
39238      * Element Containing the rendered Field (with label etc.)
39239      */
39240     /**
39241      * @cfg {Mixed} value A value to initialize this field with.
39242      */
39243     value : undefined,
39244
39245     /**
39246      * @cfg {String} name The field's HTML name attribute.
39247      */
39248     /**
39249      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39250      */
39251     // private
39252     loadedValue : false,
39253      
39254      
39255         // private ??
39256         initComponent : function(){
39257         Roo.form.Field.superclass.initComponent.call(this);
39258         this.addEvents({
39259             /**
39260              * @event focus
39261              * Fires when this field receives input focus.
39262              * @param {Roo.form.Field} this
39263              */
39264             focus : true,
39265             /**
39266              * @event blur
39267              * Fires when this field loses input focus.
39268              * @param {Roo.form.Field} this
39269              */
39270             blur : true,
39271             /**
39272              * @event specialkey
39273              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39274              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39275              * @param {Roo.form.Field} this
39276              * @param {Roo.EventObject} e The event object
39277              */
39278             specialkey : true,
39279             /**
39280              * @event change
39281              * Fires just before the field blurs if the field value has changed.
39282              * @param {Roo.form.Field} this
39283              * @param {Mixed} newValue The new value
39284              * @param {Mixed} oldValue The original value
39285              */
39286             change : true,
39287             /**
39288              * @event invalid
39289              * Fires after the field has been marked as invalid.
39290              * @param {Roo.form.Field} this
39291              * @param {String} msg The validation message
39292              */
39293             invalid : true,
39294             /**
39295              * @event valid
39296              * Fires after the field has been validated with no errors.
39297              * @param {Roo.form.Field} this
39298              */
39299             valid : true,
39300              /**
39301              * @event keyup
39302              * Fires after the key up
39303              * @param {Roo.form.Field} this
39304              * @param {Roo.EventObject}  e The event Object
39305              */
39306             keyup : true
39307         });
39308     },
39309
39310     /**
39311      * Returns the name attribute of the field if available
39312      * @return {String} name The field name
39313      */
39314     getName: function(){
39315          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39316     },
39317
39318     // private
39319     onRender : function(ct, position){
39320         Roo.form.Field.superclass.onRender.call(this, ct, position);
39321         if(!this.el){
39322             var cfg = this.getAutoCreate();
39323             if(!cfg.name){
39324                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39325             }
39326             if (!cfg.name.length) {
39327                 delete cfg.name;
39328             }
39329             if(this.inputType){
39330                 cfg.type = this.inputType;
39331             }
39332             this.el = ct.createChild(cfg, position);
39333         }
39334         var type = this.el.dom.type;
39335         if(type){
39336             if(type == 'password'){
39337                 type = 'text';
39338             }
39339             this.el.addClass('x-form-'+type);
39340         }
39341         if(this.readOnly){
39342             this.el.dom.readOnly = true;
39343         }
39344         if(this.tabIndex !== undefined){
39345             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39346         }
39347
39348         this.el.addClass([this.fieldClass, this.cls]);
39349         this.initValue();
39350     },
39351
39352     /**
39353      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39354      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39355      * @return {Roo.form.Field} this
39356      */
39357     applyTo : function(target){
39358         this.allowDomMove = false;
39359         this.el = Roo.get(target);
39360         this.render(this.el.dom.parentNode);
39361         return this;
39362     },
39363
39364     // private
39365     initValue : function(){
39366         if(this.value !== undefined){
39367             this.setValue(this.value);
39368         }else if(this.el.dom.value.length > 0){
39369             this.setValue(this.el.dom.value);
39370         }
39371     },
39372
39373     /**
39374      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39375      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39376      */
39377     isDirty : function() {
39378         if(this.disabled) {
39379             return false;
39380         }
39381         return String(this.getValue()) !== String(this.originalValue);
39382     },
39383
39384     /**
39385      * stores the current value in loadedValue
39386      */
39387     resetHasChanged : function()
39388     {
39389         this.loadedValue = String(this.getValue());
39390     },
39391     /**
39392      * checks the current value against the 'loaded' value.
39393      * Note - will return false if 'resetHasChanged' has not been called first.
39394      */
39395     hasChanged : function()
39396     {
39397         if(this.disabled || this.readOnly) {
39398             return false;
39399         }
39400         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39401     },
39402     
39403     
39404     
39405     // private
39406     afterRender : function(){
39407         Roo.form.Field.superclass.afterRender.call(this);
39408         this.initEvents();
39409     },
39410
39411     // private
39412     fireKey : function(e){
39413         //Roo.log('field ' + e.getKey());
39414         if(e.isNavKeyPress()){
39415             this.fireEvent("specialkey", this, e);
39416         }
39417     },
39418
39419     /**
39420      * Resets the current field value to the originally loaded value and clears any validation messages
39421      */
39422     reset : function(){
39423         this.setValue(this.resetValue);
39424         this.originalValue = this.getValue();
39425         this.clearInvalid();
39426     },
39427
39428     // private
39429     initEvents : function(){
39430         // safari killled keypress - so keydown is now used..
39431         this.el.on("keydown" , this.fireKey,  this);
39432         this.el.on("focus", this.onFocus,  this);
39433         this.el.on("blur", this.onBlur,  this);
39434         this.el.relayEvent('keyup', this);
39435
39436         // reference to original value for reset
39437         this.originalValue = this.getValue();
39438         this.resetValue =  this.getValue();
39439     },
39440
39441     // private
39442     onFocus : function(){
39443         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39444             this.el.addClass(this.focusClass);
39445         }
39446         if(!this.hasFocus){
39447             this.hasFocus = true;
39448             this.startValue = this.getValue();
39449             this.fireEvent("focus", this);
39450         }
39451     },
39452
39453     beforeBlur : Roo.emptyFn,
39454
39455     // private
39456     onBlur : function(){
39457         this.beforeBlur();
39458         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39459             this.el.removeClass(this.focusClass);
39460         }
39461         this.hasFocus = false;
39462         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39463             this.validate();
39464         }
39465         var v = this.getValue();
39466         if(String(v) !== String(this.startValue)){
39467             this.fireEvent('change', this, v, this.startValue);
39468         }
39469         this.fireEvent("blur", this);
39470     },
39471
39472     /**
39473      * Returns whether or not the field value is currently valid
39474      * @param {Boolean} preventMark True to disable marking the field invalid
39475      * @return {Boolean} True if the value is valid, else false
39476      */
39477     isValid : function(preventMark){
39478         if(this.disabled){
39479             return true;
39480         }
39481         var restore = this.preventMark;
39482         this.preventMark = preventMark === true;
39483         var v = this.validateValue(this.processValue(this.getRawValue()));
39484         this.preventMark = restore;
39485         return v;
39486     },
39487
39488     /**
39489      * Validates the field value
39490      * @return {Boolean} True if the value is valid, else false
39491      */
39492     validate : function(){
39493         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39494             this.clearInvalid();
39495             return true;
39496         }
39497         return false;
39498     },
39499
39500     processValue : function(value){
39501         return value;
39502     },
39503
39504     // private
39505     // Subclasses should provide the validation implementation by overriding this
39506     validateValue : function(value){
39507         return true;
39508     },
39509
39510     /**
39511      * Mark this field as invalid
39512      * @param {String} msg The validation message
39513      */
39514     markInvalid : function(msg){
39515         if(!this.rendered || this.preventMark){ // not rendered
39516             return;
39517         }
39518         
39519         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39520         
39521         obj.el.addClass(this.invalidClass);
39522         msg = msg || this.invalidText;
39523         switch(this.msgTarget){
39524             case 'qtip':
39525                 obj.el.dom.qtip = msg;
39526                 obj.el.dom.qclass = 'x-form-invalid-tip';
39527                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39528                     Roo.QuickTips.enable();
39529                 }
39530                 break;
39531             case 'title':
39532                 this.el.dom.title = msg;
39533                 break;
39534             case 'under':
39535                 if(!this.errorEl){
39536                     var elp = this.el.findParent('.x-form-element', 5, true);
39537                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39538                     this.errorEl.setWidth(elp.getWidth(true)-20);
39539                 }
39540                 this.errorEl.update(msg);
39541                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39542                 break;
39543             case 'side':
39544                 if(!this.errorIcon){
39545                     var elp = this.el.findParent('.x-form-element', 5, true);
39546                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39547                 }
39548                 this.alignErrorIcon();
39549                 this.errorIcon.dom.qtip = msg;
39550                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39551                 this.errorIcon.show();
39552                 this.on('resize', this.alignErrorIcon, this);
39553                 break;
39554             default:
39555                 var t = Roo.getDom(this.msgTarget);
39556                 t.innerHTML = msg;
39557                 t.style.display = this.msgDisplay;
39558                 break;
39559         }
39560         this.fireEvent('invalid', this, msg);
39561     },
39562
39563     // private
39564     alignErrorIcon : function(){
39565         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39566     },
39567
39568     /**
39569      * Clear any invalid styles/messages for this field
39570      */
39571     clearInvalid : function(){
39572         if(!this.rendered || this.preventMark){ // not rendered
39573             return;
39574         }
39575         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39576         
39577         obj.el.removeClass(this.invalidClass);
39578         switch(this.msgTarget){
39579             case 'qtip':
39580                 obj.el.dom.qtip = '';
39581                 break;
39582             case 'title':
39583                 this.el.dom.title = '';
39584                 break;
39585             case 'under':
39586                 if(this.errorEl){
39587                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39588                 }
39589                 break;
39590             case 'side':
39591                 if(this.errorIcon){
39592                     this.errorIcon.dom.qtip = '';
39593                     this.errorIcon.hide();
39594                     this.un('resize', this.alignErrorIcon, this);
39595                 }
39596                 break;
39597             default:
39598                 var t = Roo.getDom(this.msgTarget);
39599                 t.innerHTML = '';
39600                 t.style.display = 'none';
39601                 break;
39602         }
39603         this.fireEvent('valid', this);
39604     },
39605
39606     /**
39607      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39608      * @return {Mixed} value The field value
39609      */
39610     getRawValue : function(){
39611         var v = this.el.getValue();
39612         
39613         return v;
39614     },
39615
39616     /**
39617      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39618      * @return {Mixed} value The field value
39619      */
39620     getValue : function(){
39621         var v = this.el.getValue();
39622          
39623         return v;
39624     },
39625
39626     /**
39627      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39628      * @param {Mixed} value The value to set
39629      */
39630     setRawValue : function(v){
39631         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39632     },
39633
39634     /**
39635      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39636      * @param {Mixed} value The value to set
39637      */
39638     setValue : function(v){
39639         this.value = v;
39640         if(this.rendered){
39641             this.el.dom.value = (v === null || v === undefined ? '' : v);
39642              this.validate();
39643         }
39644     },
39645
39646     adjustSize : function(w, h){
39647         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39648         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39649         return s;
39650     },
39651
39652     adjustWidth : function(tag, w){
39653         tag = tag.toLowerCase();
39654         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39655             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39656                 if(tag == 'input'){
39657                     return w + 2;
39658                 }
39659                 if(tag == 'textarea'){
39660                     return w-2;
39661                 }
39662             }else if(Roo.isOpera){
39663                 if(tag == 'input'){
39664                     return w + 2;
39665                 }
39666                 if(tag == 'textarea'){
39667                     return w-2;
39668                 }
39669             }
39670         }
39671         return w;
39672     }
39673 });
39674
39675
39676 // anything other than normal should be considered experimental
39677 Roo.form.Field.msgFx = {
39678     normal : {
39679         show: function(msgEl, f){
39680             msgEl.setDisplayed('block');
39681         },
39682
39683         hide : function(msgEl, f){
39684             msgEl.setDisplayed(false).update('');
39685         }
39686     },
39687
39688     slide : {
39689         show: function(msgEl, f){
39690             msgEl.slideIn('t', {stopFx:true});
39691         },
39692
39693         hide : function(msgEl, f){
39694             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39695         }
39696     },
39697
39698     slideRight : {
39699         show: function(msgEl, f){
39700             msgEl.fixDisplay();
39701             msgEl.alignTo(f.el, 'tl-tr');
39702             msgEl.slideIn('l', {stopFx:true});
39703         },
39704
39705         hide : function(msgEl, f){
39706             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39707         }
39708     }
39709 };/*
39710  * Based on:
39711  * Ext JS Library 1.1.1
39712  * Copyright(c) 2006-2007, Ext JS, LLC.
39713  *
39714  * Originally Released Under LGPL - original licence link has changed is not relivant.
39715  *
39716  * Fork - LGPL
39717  * <script type="text/javascript">
39718  */
39719  
39720
39721 /**
39722  * @class Roo.form.TextField
39723  * @extends Roo.form.Field
39724  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39725  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39726  * @constructor
39727  * Creates a new TextField
39728  * @param {Object} config Configuration options
39729  */
39730 Roo.form.TextField = function(config){
39731     Roo.form.TextField.superclass.constructor.call(this, config);
39732     this.addEvents({
39733         /**
39734          * @event autosize
39735          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39736          * according to the default logic, but this event provides a hook for the developer to apply additional
39737          * logic at runtime to resize the field if needed.
39738              * @param {Roo.form.Field} this This text field
39739              * @param {Number} width The new field width
39740              */
39741         autosize : true
39742     });
39743 };
39744
39745 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39746     /**
39747      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39748      */
39749     grow : false,
39750     /**
39751      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39752      */
39753     growMin : 30,
39754     /**
39755      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39756      */
39757     growMax : 800,
39758     /**
39759      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39760      */
39761     vtype : null,
39762     /**
39763      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39764      */
39765     maskRe : null,
39766     /**
39767      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39768      */
39769     disableKeyFilter : false,
39770     /**
39771      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39772      */
39773     allowBlank : true,
39774     /**
39775      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39776      */
39777     minLength : 0,
39778     /**
39779      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39780      */
39781     maxLength : Number.MAX_VALUE,
39782     /**
39783      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39784      */
39785     minLengthText : "The minimum length for this field is {0}",
39786     /**
39787      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39788      */
39789     maxLengthText : "The maximum length for this field is {0}",
39790     /**
39791      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39792      */
39793     selectOnFocus : false,
39794     /**
39795      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39796      */    
39797     allowLeadingSpace : false,
39798     /**
39799      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39800      */
39801     blankText : "This field is required",
39802     /**
39803      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39804      * If available, this function will be called only after the basic validators all return true, and will be passed the
39805      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39806      */
39807     validator : null,
39808     /**
39809      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39810      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39811      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39812      */
39813     regex : null,
39814     /**
39815      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39816      */
39817     regexText : "",
39818     /**
39819      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39820      */
39821     emptyText : null,
39822    
39823
39824     // private
39825     initEvents : function()
39826     {
39827         if (this.emptyText) {
39828             this.el.attr('placeholder', this.emptyText);
39829         }
39830         
39831         Roo.form.TextField.superclass.initEvents.call(this);
39832         if(this.validationEvent == 'keyup'){
39833             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39834             this.el.on('keyup', this.filterValidation, this);
39835         }
39836         else if(this.validationEvent !== false){
39837             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39838         }
39839         
39840         if(this.selectOnFocus){
39841             this.on("focus", this.preFocus, this);
39842         }
39843         if (!this.allowLeadingSpace) {
39844             this.on('blur', this.cleanLeadingSpace, this);
39845         }
39846         
39847         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39848             this.el.on("keypress", this.filterKeys, this);
39849         }
39850         if(this.grow){
39851             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39852             this.el.on("click", this.autoSize,  this);
39853         }
39854         if(this.el.is('input[type=password]') && Roo.isSafari){
39855             this.el.on('keydown', this.SafariOnKeyDown, this);
39856         }
39857     },
39858
39859     processValue : function(value){
39860         if(this.stripCharsRe){
39861             var newValue = value.replace(this.stripCharsRe, '');
39862             if(newValue !== value){
39863                 this.setRawValue(newValue);
39864                 return newValue;
39865             }
39866         }
39867         return value;
39868     },
39869
39870     filterValidation : function(e){
39871         if(!e.isNavKeyPress()){
39872             this.validationTask.delay(this.validationDelay);
39873         }
39874     },
39875
39876     // private
39877     onKeyUp : function(e){
39878         if(!e.isNavKeyPress()){
39879             this.autoSize();
39880         }
39881     },
39882     // private - clean the leading white space
39883     cleanLeadingSpace : function(e)
39884     {
39885         if ( this.inputType == 'file') {
39886             return;
39887         }
39888         
39889         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39890     },
39891     /**
39892      * Resets the current field value to the originally-loaded value and clears any validation messages.
39893      *  
39894      */
39895     reset : function(){
39896         Roo.form.TextField.superclass.reset.call(this);
39897        
39898     }, 
39899     // private
39900     preFocus : function(){
39901         
39902         if(this.selectOnFocus){
39903             this.el.dom.select();
39904         }
39905     },
39906
39907     
39908     // private
39909     filterKeys : function(e){
39910         var k = e.getKey();
39911         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39912             return;
39913         }
39914         var c = e.getCharCode(), cc = String.fromCharCode(c);
39915         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39916             return;
39917         }
39918         if(!this.maskRe.test(cc)){
39919             e.stopEvent();
39920         }
39921     },
39922
39923     setValue : function(v){
39924         
39925         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39926         
39927         this.autoSize();
39928     },
39929
39930     /**
39931      * Validates a value according to the field's validation rules and marks the field as invalid
39932      * if the validation fails
39933      * @param {Mixed} value The value to validate
39934      * @return {Boolean} True if the value is valid, else false
39935      */
39936     validateValue : function(value){
39937         if(value.length < 1)  { // if it's blank
39938              if(this.allowBlank){
39939                 this.clearInvalid();
39940                 return true;
39941              }else{
39942                 this.markInvalid(this.blankText);
39943                 return false;
39944              }
39945         }
39946         if(value.length < this.minLength){
39947             this.markInvalid(String.format(this.minLengthText, this.minLength));
39948             return false;
39949         }
39950         if(value.length > this.maxLength){
39951             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39952             return false;
39953         }
39954         if(this.vtype){
39955             var vt = Roo.form.VTypes;
39956             if(!vt[this.vtype](value, this)){
39957                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39958                 return false;
39959             }
39960         }
39961         if(typeof this.validator == "function"){
39962             var msg = this.validator(value);
39963             if(msg !== true){
39964                 this.markInvalid(msg);
39965                 return false;
39966             }
39967         }
39968         if(this.regex && !this.regex.test(value)){
39969             this.markInvalid(this.regexText);
39970             return false;
39971         }
39972         return true;
39973     },
39974
39975     /**
39976      * Selects text in this field
39977      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39978      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39979      */
39980     selectText : function(start, end){
39981         var v = this.getRawValue();
39982         if(v.length > 0){
39983             start = start === undefined ? 0 : start;
39984             end = end === undefined ? v.length : end;
39985             var d = this.el.dom;
39986             if(d.setSelectionRange){
39987                 d.setSelectionRange(start, end);
39988             }else if(d.createTextRange){
39989                 var range = d.createTextRange();
39990                 range.moveStart("character", start);
39991                 range.moveEnd("character", v.length-end);
39992                 range.select();
39993             }
39994         }
39995     },
39996
39997     /**
39998      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39999      * This only takes effect if grow = true, and fires the autosize event.
40000      */
40001     autoSize : function(){
40002         if(!this.grow || !this.rendered){
40003             return;
40004         }
40005         if(!this.metrics){
40006             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40007         }
40008         var el = this.el;
40009         var v = el.dom.value;
40010         var d = document.createElement('div');
40011         d.appendChild(document.createTextNode(v));
40012         v = d.innerHTML;
40013         d = null;
40014         v += "&#160;";
40015         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40016         this.el.setWidth(w);
40017         this.fireEvent("autosize", this, w);
40018     },
40019     
40020     // private
40021     SafariOnKeyDown : function(event)
40022     {
40023         // this is a workaround for a password hang bug on chrome/ webkit.
40024         
40025         var isSelectAll = false;
40026         
40027         if(this.el.dom.selectionEnd > 0){
40028             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40029         }
40030         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40031             event.preventDefault();
40032             this.setValue('');
40033             return;
40034         }
40035         
40036         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40037             
40038             event.preventDefault();
40039             // this is very hacky as keydown always get's upper case.
40040             
40041             var cc = String.fromCharCode(event.getCharCode());
40042             
40043             
40044             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40045             
40046         }
40047         
40048         
40049     }
40050 });/*
40051  * Based on:
40052  * Ext JS Library 1.1.1
40053  * Copyright(c) 2006-2007, Ext JS, LLC.
40054  *
40055  * Originally Released Under LGPL - original licence link has changed is not relivant.
40056  *
40057  * Fork - LGPL
40058  * <script type="text/javascript">
40059  */
40060  
40061 /**
40062  * @class Roo.form.Hidden
40063  * @extends Roo.form.TextField
40064  * Simple Hidden element used on forms 
40065  * 
40066  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40067  * 
40068  * @constructor
40069  * Creates a new Hidden form element.
40070  * @param {Object} config Configuration options
40071  */
40072
40073
40074
40075 // easy hidden field...
40076 Roo.form.Hidden = function(config){
40077     Roo.form.Hidden.superclass.constructor.call(this, config);
40078 };
40079   
40080 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40081     fieldLabel:      '',
40082     inputType:      'hidden',
40083     width:          50,
40084     allowBlank:     true,
40085     labelSeparator: '',
40086     hidden:         true,
40087     itemCls :       'x-form-item-display-none'
40088
40089
40090 });
40091
40092
40093 /*
40094  * Based on:
40095  * Ext JS Library 1.1.1
40096  * Copyright(c) 2006-2007, Ext JS, LLC.
40097  *
40098  * Originally Released Under LGPL - original licence link has changed is not relivant.
40099  *
40100  * Fork - LGPL
40101  * <script type="text/javascript">
40102  */
40103  
40104 /**
40105  * @class Roo.form.TriggerField
40106  * @extends Roo.form.TextField
40107  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40108  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40109  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40110  * for which you can provide a custom implementation.  For example:
40111  * <pre><code>
40112 var trigger = new Roo.form.TriggerField();
40113 trigger.onTriggerClick = myTriggerFn;
40114 trigger.applyTo('my-field');
40115 </code></pre>
40116  *
40117  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40118  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40119  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40120  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40121  * @constructor
40122  * Create a new TriggerField.
40123  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40124  * to the base TextField)
40125  */
40126 Roo.form.TriggerField = function(config){
40127     this.mimicing = false;
40128     Roo.form.TriggerField.superclass.constructor.call(this, config);
40129 };
40130
40131 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40132     /**
40133      * @cfg {String} triggerClass A CSS class to apply to the trigger
40134      */
40135     /**
40136      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40137      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40138      */
40139     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40140     /**
40141      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40142      */
40143     hideTrigger:false,
40144
40145     /** @cfg {Boolean} grow @hide */
40146     /** @cfg {Number} growMin @hide */
40147     /** @cfg {Number} growMax @hide */
40148
40149     /**
40150      * @hide 
40151      * @method
40152      */
40153     autoSize: Roo.emptyFn,
40154     // private
40155     monitorTab : true,
40156     // private
40157     deferHeight : true,
40158
40159     
40160     actionMode : 'wrap',
40161     // private
40162     onResize : function(w, h){
40163         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40164         if(typeof w == 'number'){
40165             var x = w - this.trigger.getWidth();
40166             this.el.setWidth(this.adjustWidth('input', x));
40167             this.trigger.setStyle('left', x+'px');
40168         }
40169     },
40170
40171     // private
40172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40173
40174     // private
40175     getResizeEl : function(){
40176         return this.wrap;
40177     },
40178
40179     // private
40180     getPositionEl : function(){
40181         return this.wrap;
40182     },
40183
40184     // private
40185     alignErrorIcon : function(){
40186         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40187     },
40188
40189     // private
40190     onRender : function(ct, position){
40191         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40192         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40193         this.trigger = this.wrap.createChild(this.triggerConfig ||
40194                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40195         if(this.hideTrigger){
40196             this.trigger.setDisplayed(false);
40197         }
40198         this.initTrigger();
40199         if(!this.width){
40200             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40201         }
40202     },
40203
40204     // private
40205     initTrigger : function(){
40206         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40207         this.trigger.addClassOnOver('x-form-trigger-over');
40208         this.trigger.addClassOnClick('x-form-trigger-click');
40209     },
40210
40211     // private
40212     onDestroy : function(){
40213         if(this.trigger){
40214             this.trigger.removeAllListeners();
40215             this.trigger.remove();
40216         }
40217         if(this.wrap){
40218             this.wrap.remove();
40219         }
40220         Roo.form.TriggerField.superclass.onDestroy.call(this);
40221     },
40222
40223     // private
40224     onFocus : function(){
40225         Roo.form.TriggerField.superclass.onFocus.call(this);
40226         if(!this.mimicing){
40227             this.wrap.addClass('x-trigger-wrap-focus');
40228             this.mimicing = true;
40229             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40230             if(this.monitorTab){
40231                 this.el.on("keydown", this.checkTab, this);
40232             }
40233         }
40234     },
40235
40236     // private
40237     checkTab : function(e){
40238         if(e.getKey() == e.TAB){
40239             this.triggerBlur();
40240         }
40241     },
40242
40243     // private
40244     onBlur : function(){
40245         // do nothing
40246     },
40247
40248     // private
40249     mimicBlur : function(e, t){
40250         if(!this.wrap.contains(t) && this.validateBlur()){
40251             this.triggerBlur();
40252         }
40253     },
40254
40255     // private
40256     triggerBlur : function(){
40257         this.mimicing = false;
40258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40259         if(this.monitorTab){
40260             this.el.un("keydown", this.checkTab, this);
40261         }
40262         this.wrap.removeClass('x-trigger-wrap-focus');
40263         Roo.form.TriggerField.superclass.onBlur.call(this);
40264     },
40265
40266     // private
40267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40268     validateBlur : function(e, t){
40269         return true;
40270     },
40271
40272     // private
40273     onDisable : function(){
40274         Roo.form.TriggerField.superclass.onDisable.call(this);
40275         if(this.wrap){
40276             this.wrap.addClass('x-item-disabled');
40277         }
40278     },
40279
40280     // private
40281     onEnable : function(){
40282         Roo.form.TriggerField.superclass.onEnable.call(this);
40283         if(this.wrap){
40284             this.wrap.removeClass('x-item-disabled');
40285         }
40286     },
40287
40288     // private
40289     onShow : function(){
40290         var ae = this.getActionEl();
40291         
40292         if(ae){
40293             ae.dom.style.display = '';
40294             ae.dom.style.visibility = 'visible';
40295         }
40296     },
40297
40298     // private
40299     
40300     onHide : function(){
40301         var ae = this.getActionEl();
40302         ae.dom.style.display = 'none';
40303     },
40304
40305     /**
40306      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40307      * by an implementing function.
40308      * @method
40309      * @param {EventObject} e
40310      */
40311     onTriggerClick : Roo.emptyFn
40312 });
40313
40314 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40315 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40316 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40317 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40318     initComponent : function(){
40319         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40320
40321         this.triggerConfig = {
40322             tag:'span', cls:'x-form-twin-triggers', cn:[
40323             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40324             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40325         ]};
40326     },
40327
40328     getTrigger : function(index){
40329         return this.triggers[index];
40330     },
40331
40332     initTrigger : function(){
40333         var ts = this.trigger.select('.x-form-trigger', true);
40334         this.wrap.setStyle('overflow', 'hidden');
40335         var triggerField = this;
40336         ts.each(function(t, all, index){
40337             t.hide = function(){
40338                 var w = triggerField.wrap.getWidth();
40339                 this.dom.style.display = 'none';
40340                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40341             };
40342             t.show = function(){
40343                 var w = triggerField.wrap.getWidth();
40344                 this.dom.style.display = '';
40345                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40346             };
40347             var triggerIndex = 'Trigger'+(index+1);
40348
40349             if(this['hide'+triggerIndex]){
40350                 t.dom.style.display = 'none';
40351             }
40352             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40353             t.addClassOnOver('x-form-trigger-over');
40354             t.addClassOnClick('x-form-trigger-click');
40355         }, this);
40356         this.triggers = ts.elements;
40357     },
40358
40359     onTrigger1Click : Roo.emptyFn,
40360     onTrigger2Click : Roo.emptyFn
40361 });/*
40362  * Based on:
40363  * Ext JS Library 1.1.1
40364  * Copyright(c) 2006-2007, Ext JS, LLC.
40365  *
40366  * Originally Released Under LGPL - original licence link has changed is not relivant.
40367  *
40368  * Fork - LGPL
40369  * <script type="text/javascript">
40370  */
40371  
40372 /**
40373  * @class Roo.form.TextArea
40374  * @extends Roo.form.TextField
40375  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40376  * support for auto-sizing.
40377  * @constructor
40378  * Creates a new TextArea
40379  * @param {Object} config Configuration options
40380  */
40381 Roo.form.TextArea = function(config){
40382     Roo.form.TextArea.superclass.constructor.call(this, config);
40383     // these are provided exchanges for backwards compat
40384     // minHeight/maxHeight were replaced by growMin/growMax to be
40385     // compatible with TextField growing config values
40386     if(this.minHeight !== undefined){
40387         this.growMin = this.minHeight;
40388     }
40389     if(this.maxHeight !== undefined){
40390         this.growMax = this.maxHeight;
40391     }
40392 };
40393
40394 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40395     /**
40396      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40397      */
40398     growMin : 60,
40399     /**
40400      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40401      */
40402     growMax: 1000,
40403     /**
40404      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40405      * in the field (equivalent to setting overflow: hidden, defaults to false)
40406      */
40407     preventScrollbars: false,
40408     /**
40409      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40410      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40411      */
40412
40413     // private
40414     onRender : function(ct, position){
40415         if(!this.el){
40416             this.defaultAutoCreate = {
40417                 tag: "textarea",
40418                 style:"width:300px;height:60px;",
40419                 autocomplete: "new-password"
40420             };
40421         }
40422         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40423         if(this.grow){
40424             this.textSizeEl = Roo.DomHelper.append(document.body, {
40425                 tag: "pre", cls: "x-form-grow-sizer"
40426             });
40427             if(this.preventScrollbars){
40428                 this.el.setStyle("overflow", "hidden");
40429             }
40430             this.el.setHeight(this.growMin);
40431         }
40432     },
40433
40434     onDestroy : function(){
40435         if(this.textSizeEl){
40436             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40437         }
40438         Roo.form.TextArea.superclass.onDestroy.call(this);
40439     },
40440
40441     // private
40442     onKeyUp : function(e){
40443         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40444             this.autoSize();
40445         }
40446     },
40447
40448     /**
40449      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40450      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40451      */
40452     autoSize : function(){
40453         if(!this.grow || !this.textSizeEl){
40454             return;
40455         }
40456         var el = this.el;
40457         var v = el.dom.value;
40458         var ts = this.textSizeEl;
40459
40460         ts.innerHTML = '';
40461         ts.appendChild(document.createTextNode(v));
40462         v = ts.innerHTML;
40463
40464         Roo.fly(ts).setWidth(this.el.getWidth());
40465         if(v.length < 1){
40466             v = "&#160;&#160;";
40467         }else{
40468             if(Roo.isIE){
40469                 v = v.replace(/\n/g, '<p>&#160;</p>');
40470             }
40471             v += "&#160;\n&#160;";
40472         }
40473         ts.innerHTML = v;
40474         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40475         if(h != this.lastHeight){
40476             this.lastHeight = h;
40477             this.el.setHeight(h);
40478             this.fireEvent("autosize", this, h);
40479         }
40480     }
40481 });/*
40482  * Based on:
40483  * Ext JS Library 1.1.1
40484  * Copyright(c) 2006-2007, Ext JS, LLC.
40485  *
40486  * Originally Released Under LGPL - original licence link has changed is not relivant.
40487  *
40488  * Fork - LGPL
40489  * <script type="text/javascript">
40490  */
40491  
40492
40493 /**
40494  * @class Roo.form.NumberField
40495  * @extends Roo.form.TextField
40496  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40497  * @constructor
40498  * Creates a new NumberField
40499  * @param {Object} config Configuration options
40500  */
40501 Roo.form.NumberField = function(config){
40502     Roo.form.NumberField.superclass.constructor.call(this, config);
40503 };
40504
40505 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40506     /**
40507      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40508      */
40509     fieldClass: "x-form-field x-form-num-field",
40510     /**
40511      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40512      */
40513     allowDecimals : true,
40514     /**
40515      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40516      */
40517     decimalSeparator : ".",
40518     /**
40519      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40520      */
40521     decimalPrecision : 2,
40522     /**
40523      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40524      */
40525     allowNegative : true,
40526     /**
40527      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40528      */
40529     minValue : Number.NEGATIVE_INFINITY,
40530     /**
40531      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40532      */
40533     maxValue : Number.MAX_VALUE,
40534     /**
40535      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40536      */
40537     minText : "The minimum value for this field is {0}",
40538     /**
40539      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40540      */
40541     maxText : "The maximum value for this field is {0}",
40542     /**
40543      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40544      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40545      */
40546     nanText : "{0} is not a valid number",
40547
40548     // private
40549     initEvents : function(){
40550         Roo.form.NumberField.superclass.initEvents.call(this);
40551         var allowed = "0123456789";
40552         if(this.allowDecimals){
40553             allowed += this.decimalSeparator;
40554         }
40555         if(this.allowNegative){
40556             allowed += "-";
40557         }
40558         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40559         var keyPress = function(e){
40560             var k = e.getKey();
40561             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40562                 return;
40563             }
40564             var c = e.getCharCode();
40565             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40566                 e.stopEvent();
40567             }
40568         };
40569         this.el.on("keypress", keyPress, this);
40570     },
40571
40572     // private
40573     validateValue : function(value){
40574         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40575             return false;
40576         }
40577         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40578              return true;
40579         }
40580         var num = this.parseValue(value);
40581         if(isNaN(num)){
40582             this.markInvalid(String.format(this.nanText, value));
40583             return false;
40584         }
40585         if(num < this.minValue){
40586             this.markInvalid(String.format(this.minText, this.minValue));
40587             return false;
40588         }
40589         if(num > this.maxValue){
40590             this.markInvalid(String.format(this.maxText, this.maxValue));
40591             return false;
40592         }
40593         return true;
40594     },
40595
40596     getValue : function(){
40597         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40598     },
40599
40600     // private
40601     parseValue : function(value){
40602         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40603         return isNaN(value) ? '' : value;
40604     },
40605
40606     // private
40607     fixPrecision : function(value){
40608         var nan = isNaN(value);
40609         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40610             return nan ? '' : value;
40611         }
40612         return parseFloat(value).toFixed(this.decimalPrecision);
40613     },
40614
40615     setValue : function(v){
40616         v = this.fixPrecision(v);
40617         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40618     },
40619
40620     // private
40621     decimalPrecisionFcn : function(v){
40622         return Math.floor(v);
40623     },
40624
40625     beforeBlur : function(){
40626         var v = this.parseValue(this.getRawValue());
40627         if(v){
40628             this.setValue(v);
40629         }
40630     }
40631 });/*
40632  * Based on:
40633  * Ext JS Library 1.1.1
40634  * Copyright(c) 2006-2007, Ext JS, LLC.
40635  *
40636  * Originally Released Under LGPL - original licence link has changed is not relivant.
40637  *
40638  * Fork - LGPL
40639  * <script type="text/javascript">
40640  */
40641  
40642 /**
40643  * @class Roo.form.DateField
40644  * @extends Roo.form.TriggerField
40645  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40646 * @constructor
40647 * Create a new DateField
40648 * @param {Object} config
40649  */
40650 Roo.form.DateField = function(config)
40651 {
40652     Roo.form.DateField.superclass.constructor.call(this, config);
40653     
40654       this.addEvents({
40655          
40656         /**
40657          * @event select
40658          * Fires when a date is selected
40659              * @param {Roo.form.DateField} combo This combo box
40660              * @param {Date} date The date selected
40661              */
40662         'select' : true
40663          
40664     });
40665     
40666     
40667     if(typeof this.minValue == "string") {
40668         this.minValue = this.parseDate(this.minValue);
40669     }
40670     if(typeof this.maxValue == "string") {
40671         this.maxValue = this.parseDate(this.maxValue);
40672     }
40673     this.ddMatch = null;
40674     if(this.disabledDates){
40675         var dd = this.disabledDates;
40676         var re = "(?:";
40677         for(var i = 0; i < dd.length; i++){
40678             re += dd[i];
40679             if(i != dd.length-1) {
40680                 re += "|";
40681             }
40682         }
40683         this.ddMatch = new RegExp(re + ")");
40684     }
40685 };
40686
40687 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40688     /**
40689      * @cfg {String} format
40690      * The default date format string which can be overriden for localization support.  The format must be
40691      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40692      */
40693     format : "m/d/y",
40694     /**
40695      * @cfg {String} altFormats
40696      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40697      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40698      */
40699     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40700     /**
40701      * @cfg {Array} disabledDays
40702      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40703      */
40704     disabledDays : null,
40705     /**
40706      * @cfg {String} disabledDaysText
40707      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40708      */
40709     disabledDaysText : "Disabled",
40710     /**
40711      * @cfg {Array} disabledDates
40712      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40713      * expression so they are very powerful. Some examples:
40714      * <ul>
40715      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40716      * <li>["03/08", "09/16"] would disable those days for every year</li>
40717      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40718      * <li>["03/../2006"] would disable every day in March 2006</li>
40719      * <li>["^03"] would disable every day in every March</li>
40720      * </ul>
40721      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40722      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40723      */
40724     disabledDates : null,
40725     /**
40726      * @cfg {String} disabledDatesText
40727      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40728      */
40729     disabledDatesText : "Disabled",
40730     /**
40731      * @cfg {Date/String} minValue
40732      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40733      * valid format (defaults to null).
40734      */
40735     minValue : null,
40736     /**
40737      * @cfg {Date/String} maxValue
40738      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40739      * valid format (defaults to null).
40740      */
40741     maxValue : null,
40742     /**
40743      * @cfg {String} minText
40744      * The error text to display when the date in the cell is before minValue (defaults to
40745      * 'The date in this field must be after {minValue}').
40746      */
40747     minText : "The date in this field must be equal to or after {0}",
40748     /**
40749      * @cfg {String} maxText
40750      * The error text to display when the date in the cell is after maxValue (defaults to
40751      * 'The date in this field must be before {maxValue}').
40752      */
40753     maxText : "The date in this field must be equal to or before {0}",
40754     /**
40755      * @cfg {String} invalidText
40756      * The error text to display when the date in the field is invalid (defaults to
40757      * '{value} is not a valid date - it must be in the format {format}').
40758      */
40759     invalidText : "{0} is not a valid date - it must be in the format {1}",
40760     /**
40761      * @cfg {String} triggerClass
40762      * An additional CSS class used to style the trigger button.  The trigger will always get the
40763      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40764      * which displays a calendar icon).
40765      */
40766     triggerClass : 'x-form-date-trigger',
40767     
40768
40769     /**
40770      * @cfg {Boolean} useIso
40771      * if enabled, then the date field will use a hidden field to store the 
40772      * real value as iso formated date. default (false)
40773      */ 
40774     useIso : false,
40775     /**
40776      * @cfg {String/Object} autoCreate
40777      * A DomHelper element spec, or true for a default element spec (defaults to
40778      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40779      */ 
40780     // private
40781     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40782     
40783     // private
40784     hiddenField: false,
40785     
40786     onRender : function(ct, position)
40787     {
40788         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40789         if (this.useIso) {
40790             //this.el.dom.removeAttribute('name'); 
40791             Roo.log("Changing name?");
40792             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40793             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40794                     'before', true);
40795             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40796             // prevent input submission
40797             this.hiddenName = this.name;
40798         }
40799             
40800             
40801     },
40802     
40803     // private
40804     validateValue : function(value)
40805     {
40806         value = this.formatDate(value);
40807         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40808             Roo.log('super failed');
40809             return false;
40810         }
40811         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40812              return true;
40813         }
40814         var svalue = value;
40815         value = this.parseDate(value);
40816         if(!value){
40817             Roo.log('parse date failed' + svalue);
40818             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40819             return false;
40820         }
40821         var time = value.getTime();
40822         if(this.minValue && time < this.minValue.getTime()){
40823             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40824             return false;
40825         }
40826         if(this.maxValue && time > this.maxValue.getTime()){
40827             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40828             return false;
40829         }
40830         if(this.disabledDays){
40831             var day = value.getDay();
40832             for(var i = 0; i < this.disabledDays.length; i++) {
40833                 if(day === this.disabledDays[i]){
40834                     this.markInvalid(this.disabledDaysText);
40835                     return false;
40836                 }
40837             }
40838         }
40839         var fvalue = this.formatDate(value);
40840         if(this.ddMatch && this.ddMatch.test(fvalue)){
40841             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40842             return false;
40843         }
40844         return true;
40845     },
40846
40847     // private
40848     // Provides logic to override the default TriggerField.validateBlur which just returns true
40849     validateBlur : function(){
40850         return !this.menu || !this.menu.isVisible();
40851     },
40852     
40853     getName: function()
40854     {
40855         // returns hidden if it's set..
40856         if (!this.rendered) {return ''};
40857         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40858         
40859     },
40860
40861     /**
40862      * Returns the current date value of the date field.
40863      * @return {Date} The date value
40864      */
40865     getValue : function(){
40866         
40867         return  this.hiddenField ?
40868                 this.hiddenField.value :
40869                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40870     },
40871
40872     /**
40873      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40874      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40875      * (the default format used is "m/d/y").
40876      * <br />Usage:
40877      * <pre><code>
40878 //All of these calls set the same date value (May 4, 2006)
40879
40880 //Pass a date object:
40881 var dt = new Date('5/4/06');
40882 dateField.setValue(dt);
40883
40884 //Pass a date string (default format):
40885 dateField.setValue('5/4/06');
40886
40887 //Pass a date string (custom format):
40888 dateField.format = 'Y-m-d';
40889 dateField.setValue('2006-5-4');
40890 </code></pre>
40891      * @param {String/Date} date The date or valid date string
40892      */
40893     setValue : function(date){
40894         if (this.hiddenField) {
40895             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40896         }
40897         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40898         // make sure the value field is always stored as a date..
40899         this.value = this.parseDate(date);
40900         
40901         
40902     },
40903
40904     // private
40905     parseDate : function(value){
40906         if(!value || value instanceof Date){
40907             return value;
40908         }
40909         var v = Date.parseDate(value, this.format);
40910          if (!v && this.useIso) {
40911             v = Date.parseDate(value, 'Y-m-d');
40912         }
40913         if(!v && this.altFormats){
40914             if(!this.altFormatsArray){
40915                 this.altFormatsArray = this.altFormats.split("|");
40916             }
40917             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40918                 v = Date.parseDate(value, this.altFormatsArray[i]);
40919             }
40920         }
40921         return v;
40922     },
40923
40924     // private
40925     formatDate : function(date, fmt){
40926         return (!date || !(date instanceof Date)) ?
40927                date : date.dateFormat(fmt || this.format);
40928     },
40929
40930     // private
40931     menuListeners : {
40932         select: function(m, d){
40933             
40934             this.setValue(d);
40935             this.fireEvent('select', this, d);
40936         },
40937         show : function(){ // retain focus styling
40938             this.onFocus();
40939         },
40940         hide : function(){
40941             this.focus.defer(10, this);
40942             var ml = this.menuListeners;
40943             this.menu.un("select", ml.select,  this);
40944             this.menu.un("show", ml.show,  this);
40945             this.menu.un("hide", ml.hide,  this);
40946         }
40947     },
40948
40949     // private
40950     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40951     onTriggerClick : function(){
40952         if(this.disabled){
40953             return;
40954         }
40955         if(this.menu == null){
40956             this.menu = new Roo.menu.DateMenu();
40957         }
40958         Roo.apply(this.menu.picker,  {
40959             showClear: this.allowBlank,
40960             minDate : this.minValue,
40961             maxDate : this.maxValue,
40962             disabledDatesRE : this.ddMatch,
40963             disabledDatesText : this.disabledDatesText,
40964             disabledDays : this.disabledDays,
40965             disabledDaysText : this.disabledDaysText,
40966             format : this.useIso ? 'Y-m-d' : this.format,
40967             minText : String.format(this.minText, this.formatDate(this.minValue)),
40968             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40969         });
40970         this.menu.on(Roo.apply({}, this.menuListeners, {
40971             scope:this
40972         }));
40973         this.menu.picker.setValue(this.getValue() || new Date());
40974         this.menu.show(this.el, "tl-bl?");
40975     },
40976
40977     beforeBlur : function(){
40978         var v = this.parseDate(this.getRawValue());
40979         if(v){
40980             this.setValue(v);
40981         }
40982     },
40983
40984     /*@
40985      * overide
40986      * 
40987      */
40988     isDirty : function() {
40989         if(this.disabled) {
40990             return false;
40991         }
40992         
40993         if(typeof(this.startValue) === 'undefined'){
40994             return false;
40995         }
40996         
40997         return String(this.getValue()) !== String(this.startValue);
40998         
40999     },
41000     // @overide
41001     cleanLeadingSpace : function(e)
41002     {
41003        return;
41004     }
41005     
41006 });/*
41007  * Based on:
41008  * Ext JS Library 1.1.1
41009  * Copyright(c) 2006-2007, Ext JS, LLC.
41010  *
41011  * Originally Released Under LGPL - original licence link has changed is not relivant.
41012  *
41013  * Fork - LGPL
41014  * <script type="text/javascript">
41015  */
41016  
41017 /**
41018  * @class Roo.form.MonthField
41019  * @extends Roo.form.TriggerField
41020  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41021 * @constructor
41022 * Create a new MonthField
41023 * @param {Object} config
41024  */
41025 Roo.form.MonthField = function(config){
41026     
41027     Roo.form.MonthField.superclass.constructor.call(this, config);
41028     
41029       this.addEvents({
41030          
41031         /**
41032          * @event select
41033          * Fires when a date is selected
41034              * @param {Roo.form.MonthFieeld} combo This combo box
41035              * @param {Date} date The date selected
41036              */
41037         'select' : true
41038          
41039     });
41040     
41041     
41042     if(typeof this.minValue == "string") {
41043         this.minValue = this.parseDate(this.minValue);
41044     }
41045     if(typeof this.maxValue == "string") {
41046         this.maxValue = this.parseDate(this.maxValue);
41047     }
41048     this.ddMatch = null;
41049     if(this.disabledDates){
41050         var dd = this.disabledDates;
41051         var re = "(?:";
41052         for(var i = 0; i < dd.length; i++){
41053             re += dd[i];
41054             if(i != dd.length-1) {
41055                 re += "|";
41056             }
41057         }
41058         this.ddMatch = new RegExp(re + ")");
41059     }
41060 };
41061
41062 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41063     /**
41064      * @cfg {String} format
41065      * The default date format string which can be overriden for localization support.  The format must be
41066      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41067      */
41068     format : "M Y",
41069     /**
41070      * @cfg {String} altFormats
41071      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41072      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41073      */
41074     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41075     /**
41076      * @cfg {Array} disabledDays
41077      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41078      */
41079     disabledDays : [0,1,2,3,4,5,6],
41080     /**
41081      * @cfg {String} disabledDaysText
41082      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41083      */
41084     disabledDaysText : "Disabled",
41085     /**
41086      * @cfg {Array} disabledDates
41087      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41088      * expression so they are very powerful. Some examples:
41089      * <ul>
41090      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41091      * <li>["03/08", "09/16"] would disable those days for every year</li>
41092      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41093      * <li>["03/../2006"] would disable every day in March 2006</li>
41094      * <li>["^03"] would disable every day in every March</li>
41095      * </ul>
41096      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41097      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41098      */
41099     disabledDates : null,
41100     /**
41101      * @cfg {String} disabledDatesText
41102      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41103      */
41104     disabledDatesText : "Disabled",
41105     /**
41106      * @cfg {Date/String} minValue
41107      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41108      * valid format (defaults to null).
41109      */
41110     minValue : null,
41111     /**
41112      * @cfg {Date/String} maxValue
41113      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41114      * valid format (defaults to null).
41115      */
41116     maxValue : null,
41117     /**
41118      * @cfg {String} minText
41119      * The error text to display when the date in the cell is before minValue (defaults to
41120      * 'The date in this field must be after {minValue}').
41121      */
41122     minText : "The date in this field must be equal to or after {0}",
41123     /**
41124      * @cfg {String} maxTextf
41125      * The error text to display when the date in the cell is after maxValue (defaults to
41126      * 'The date in this field must be before {maxValue}').
41127      */
41128     maxText : "The date in this field must be equal to or before {0}",
41129     /**
41130      * @cfg {String} invalidText
41131      * The error text to display when the date in the field is invalid (defaults to
41132      * '{value} is not a valid date - it must be in the format {format}').
41133      */
41134     invalidText : "{0} is not a valid date - it must be in the format {1}",
41135     /**
41136      * @cfg {String} triggerClass
41137      * An additional CSS class used to style the trigger button.  The trigger will always get the
41138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41139      * which displays a calendar icon).
41140      */
41141     triggerClass : 'x-form-date-trigger',
41142     
41143
41144     /**
41145      * @cfg {Boolean} useIso
41146      * if enabled, then the date field will use a hidden field to store the 
41147      * real value as iso formated date. default (true)
41148      */ 
41149     useIso : true,
41150     /**
41151      * @cfg {String/Object} autoCreate
41152      * A DomHelper element spec, or true for a default element spec (defaults to
41153      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41154      */ 
41155     // private
41156     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41157     
41158     // private
41159     hiddenField: false,
41160     
41161     hideMonthPicker : false,
41162     
41163     onRender : function(ct, position)
41164     {
41165         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41166         if (this.useIso) {
41167             this.el.dom.removeAttribute('name'); 
41168             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41169                     'before', true);
41170             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41171             // prevent input submission
41172             this.hiddenName = this.name;
41173         }
41174             
41175             
41176     },
41177     
41178     // private
41179     validateValue : function(value)
41180     {
41181         value = this.formatDate(value);
41182         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41183             return false;
41184         }
41185         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41186              return true;
41187         }
41188         var svalue = value;
41189         value = this.parseDate(value);
41190         if(!value){
41191             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41192             return false;
41193         }
41194         var time = value.getTime();
41195         if(this.minValue && time < this.minValue.getTime()){
41196             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41197             return false;
41198         }
41199         if(this.maxValue && time > this.maxValue.getTime()){
41200             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41201             return false;
41202         }
41203         /*if(this.disabledDays){
41204             var day = value.getDay();
41205             for(var i = 0; i < this.disabledDays.length; i++) {
41206                 if(day === this.disabledDays[i]){
41207                     this.markInvalid(this.disabledDaysText);
41208                     return false;
41209                 }
41210             }
41211         }
41212         */
41213         var fvalue = this.formatDate(value);
41214         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41215             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41216             return false;
41217         }
41218         */
41219         return true;
41220     },
41221
41222     // private
41223     // Provides logic to override the default TriggerField.validateBlur which just returns true
41224     validateBlur : function(){
41225         return !this.menu || !this.menu.isVisible();
41226     },
41227
41228     /**
41229      * Returns the current date value of the date field.
41230      * @return {Date} The date value
41231      */
41232     getValue : function(){
41233         
41234         
41235         
41236         return  this.hiddenField ?
41237                 this.hiddenField.value :
41238                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41239     },
41240
41241     /**
41242      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41243      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41244      * (the default format used is "m/d/y").
41245      * <br />Usage:
41246      * <pre><code>
41247 //All of these calls set the same date value (May 4, 2006)
41248
41249 //Pass a date object:
41250 var dt = new Date('5/4/06');
41251 monthField.setValue(dt);
41252
41253 //Pass a date string (default format):
41254 monthField.setValue('5/4/06');
41255
41256 //Pass a date string (custom format):
41257 monthField.format = 'Y-m-d';
41258 monthField.setValue('2006-5-4');
41259 </code></pre>
41260      * @param {String/Date} date The date or valid date string
41261      */
41262     setValue : function(date){
41263         Roo.log('month setValue' + date);
41264         // can only be first of month..
41265         
41266         var val = this.parseDate(date);
41267         
41268         if (this.hiddenField) {
41269             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41270         }
41271         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41272         this.value = this.parseDate(date);
41273     },
41274
41275     // private
41276     parseDate : function(value){
41277         if(!value || value instanceof Date){
41278             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41279             return value;
41280         }
41281         var v = Date.parseDate(value, this.format);
41282         if (!v && this.useIso) {
41283             v = Date.parseDate(value, 'Y-m-d');
41284         }
41285         if (v) {
41286             // 
41287             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41288         }
41289         
41290         
41291         if(!v && this.altFormats){
41292             if(!this.altFormatsArray){
41293                 this.altFormatsArray = this.altFormats.split("|");
41294             }
41295             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41296                 v = Date.parseDate(value, this.altFormatsArray[i]);
41297             }
41298         }
41299         return v;
41300     },
41301
41302     // private
41303     formatDate : function(date, fmt){
41304         return (!date || !(date instanceof Date)) ?
41305                date : date.dateFormat(fmt || this.format);
41306     },
41307
41308     // private
41309     menuListeners : {
41310         select: function(m, d){
41311             this.setValue(d);
41312             this.fireEvent('select', this, d);
41313         },
41314         show : function(){ // retain focus styling
41315             this.onFocus();
41316         },
41317         hide : function(){
41318             this.focus.defer(10, this);
41319             var ml = this.menuListeners;
41320             this.menu.un("select", ml.select,  this);
41321             this.menu.un("show", ml.show,  this);
41322             this.menu.un("hide", ml.hide,  this);
41323         }
41324     },
41325     // private
41326     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41327     onTriggerClick : function(){
41328         if(this.disabled){
41329             return;
41330         }
41331         if(this.menu == null){
41332             this.menu = new Roo.menu.DateMenu();
41333            
41334         }
41335         
41336         Roo.apply(this.menu.picker,  {
41337             
41338             showClear: this.allowBlank,
41339             minDate : this.minValue,
41340             maxDate : this.maxValue,
41341             disabledDatesRE : this.ddMatch,
41342             disabledDatesText : this.disabledDatesText,
41343             
41344             format : this.useIso ? 'Y-m-d' : this.format,
41345             minText : String.format(this.minText, this.formatDate(this.minValue)),
41346             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41347             
41348         });
41349          this.menu.on(Roo.apply({}, this.menuListeners, {
41350             scope:this
41351         }));
41352        
41353         
41354         var m = this.menu;
41355         var p = m.picker;
41356         
41357         // hide month picker get's called when we called by 'before hide';
41358         
41359         var ignorehide = true;
41360         p.hideMonthPicker  = function(disableAnim){
41361             if (ignorehide) {
41362                 return;
41363             }
41364              if(this.monthPicker){
41365                 Roo.log("hideMonthPicker called");
41366                 if(disableAnim === true){
41367                     this.monthPicker.hide();
41368                 }else{
41369                     this.monthPicker.slideOut('t', {duration:.2});
41370                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41371                     p.fireEvent("select", this, this.value);
41372                     m.hide();
41373                 }
41374             }
41375         }
41376         
41377         Roo.log('picker set value');
41378         Roo.log(this.getValue());
41379         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41380         m.show(this.el, 'tl-bl?');
41381         ignorehide  = false;
41382         // this will trigger hideMonthPicker..
41383         
41384         
41385         // hidden the day picker
41386         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41387         
41388         
41389         
41390       
41391         
41392         p.showMonthPicker.defer(100, p);
41393     
41394         
41395        
41396     },
41397
41398     beforeBlur : function(){
41399         var v = this.parseDate(this.getRawValue());
41400         if(v){
41401             this.setValue(v);
41402         }
41403     }
41404
41405     /** @cfg {Boolean} grow @hide */
41406     /** @cfg {Number} growMin @hide */
41407     /** @cfg {Number} growMax @hide */
41408     /**
41409      * @hide
41410      * @method autoSize
41411      */
41412 });/*
41413  * Based on:
41414  * Ext JS Library 1.1.1
41415  * Copyright(c) 2006-2007, Ext JS, LLC.
41416  *
41417  * Originally Released Under LGPL - original licence link has changed is not relivant.
41418  *
41419  * Fork - LGPL
41420  * <script type="text/javascript">
41421  */
41422  
41423
41424 /**
41425  * @class Roo.form.ComboBox
41426  * @extends Roo.form.TriggerField
41427  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41428  * @constructor
41429  * Create a new ComboBox.
41430  * @param {Object} config Configuration options
41431  */
41432 Roo.form.ComboBox = function(config){
41433     Roo.form.ComboBox.superclass.constructor.call(this, config);
41434     this.addEvents({
41435         /**
41436          * @event expand
41437          * Fires when the dropdown list is expanded
41438              * @param {Roo.form.ComboBox} combo This combo box
41439              */
41440         'expand' : true,
41441         /**
41442          * @event collapse
41443          * Fires when the dropdown list is collapsed
41444              * @param {Roo.form.ComboBox} combo This combo box
41445              */
41446         'collapse' : true,
41447         /**
41448          * @event beforeselect
41449          * Fires before a list item is selected. Return false to cancel the selection.
41450              * @param {Roo.form.ComboBox} combo This combo box
41451              * @param {Roo.data.Record} record The data record returned from the underlying store
41452              * @param {Number} index The index of the selected item in the dropdown list
41453              */
41454         'beforeselect' : true,
41455         /**
41456          * @event select
41457          * Fires when a list item is selected
41458              * @param {Roo.form.ComboBox} combo This combo box
41459              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41460              * @param {Number} index The index of the selected item in the dropdown list
41461              */
41462         'select' : true,
41463         /**
41464          * @event beforequery
41465          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41466          * The event object passed has these properties:
41467              * @param {Roo.form.ComboBox} combo This combo box
41468              * @param {String} query The query
41469              * @param {Boolean} forceAll true to force "all" query
41470              * @param {Boolean} cancel true to cancel the query
41471              * @param {Object} e The query event object
41472              */
41473         'beforequery': true,
41474          /**
41475          * @event add
41476          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41477              * @param {Roo.form.ComboBox} combo This combo box
41478              */
41479         'add' : true,
41480         /**
41481          * @event edit
41482          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41483              * @param {Roo.form.ComboBox} combo This combo box
41484              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41485              */
41486         'edit' : true
41487         
41488         
41489     });
41490     if(this.transform){
41491         this.allowDomMove = false;
41492         var s = Roo.getDom(this.transform);
41493         if(!this.hiddenName){
41494             this.hiddenName = s.name;
41495         }
41496         if(!this.store){
41497             this.mode = 'local';
41498             var d = [], opts = s.options;
41499             for(var i = 0, len = opts.length;i < len; i++){
41500                 var o = opts[i];
41501                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41502                 if(o.selected) {
41503                     this.value = value;
41504                 }
41505                 d.push([value, o.text]);
41506             }
41507             this.store = new Roo.data.SimpleStore({
41508                 'id': 0,
41509                 fields: ['value', 'text'],
41510                 data : d
41511             });
41512             this.valueField = 'value';
41513             this.displayField = 'text';
41514         }
41515         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41516         if(!this.lazyRender){
41517             this.target = true;
41518             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41519             s.parentNode.removeChild(s); // remove it
41520             this.render(this.el.parentNode);
41521         }else{
41522             s.parentNode.removeChild(s); // remove it
41523         }
41524
41525     }
41526     if (this.store) {
41527         this.store = Roo.factory(this.store, Roo.data);
41528     }
41529     
41530     this.selectedIndex = -1;
41531     if(this.mode == 'local'){
41532         if(config.queryDelay === undefined){
41533             this.queryDelay = 10;
41534         }
41535         if(config.minChars === undefined){
41536             this.minChars = 0;
41537         }
41538     }
41539 };
41540
41541 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41542     /**
41543      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41544      */
41545     /**
41546      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41547      * rendering into an Roo.Editor, defaults to false)
41548      */
41549     /**
41550      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41551      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41552      */
41553     /**
41554      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41555      */
41556     /**
41557      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41558      * the dropdown list (defaults to undefined, with no header element)
41559      */
41560
41561      /**
41562      * @cfg {String/Roo.Template} tpl The template to use to render the output
41563      */
41564      
41565     // private
41566     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41567     /**
41568      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41569      */
41570     listWidth: undefined,
41571     /**
41572      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41573      * mode = 'remote' or 'text' if mode = 'local')
41574      */
41575     displayField: undefined,
41576     /**
41577      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41578      * mode = 'remote' or 'value' if mode = 'local'). 
41579      * Note: use of a valueField requires the user make a selection
41580      * in order for a value to be mapped.
41581      */
41582     valueField: undefined,
41583     
41584     
41585     /**
41586      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41587      * field's data value (defaults to the underlying DOM element's name)
41588      */
41589     hiddenName: undefined,
41590     /**
41591      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41592      */
41593     listClass: '',
41594     /**
41595      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41596      */
41597     selectedClass: 'x-combo-selected',
41598     /**
41599      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41600      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41601      * which displays a downward arrow icon).
41602      */
41603     triggerClass : 'x-form-arrow-trigger',
41604     /**
41605      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41606      */
41607     shadow:'sides',
41608     /**
41609      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41610      * anchor positions (defaults to 'tl-bl')
41611      */
41612     listAlign: 'tl-bl?',
41613     /**
41614      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41615      */
41616     maxHeight: 300,
41617     /**
41618      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41619      * query specified by the allQuery config option (defaults to 'query')
41620      */
41621     triggerAction: 'query',
41622     /**
41623      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41624      * (defaults to 4, does not apply if editable = false)
41625      */
41626     minChars : 4,
41627     /**
41628      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41629      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41630      */
41631     typeAhead: false,
41632     /**
41633      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41634      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41635      */
41636     queryDelay: 500,
41637     /**
41638      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41639      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41640      */
41641     pageSize: 0,
41642     /**
41643      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41644      * when editable = true (defaults to false)
41645      */
41646     selectOnFocus:false,
41647     /**
41648      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41649      */
41650     queryParam: 'query',
41651     /**
41652      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41653      * when mode = 'remote' (defaults to 'Loading...')
41654      */
41655     loadingText: 'Loading...',
41656     /**
41657      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41658      */
41659     resizable: false,
41660     /**
41661      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41662      */
41663     handleHeight : 8,
41664     /**
41665      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41666      * traditional select (defaults to true)
41667      */
41668     editable: true,
41669     /**
41670      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41671      */
41672     allQuery: '',
41673     /**
41674      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41675      */
41676     mode: 'remote',
41677     /**
41678      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41679      * listWidth has a higher value)
41680      */
41681     minListWidth : 70,
41682     /**
41683      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41684      * allow the user to set arbitrary text into the field (defaults to false)
41685      */
41686     forceSelection:false,
41687     /**
41688      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41689      * if typeAhead = true (defaults to 250)
41690      */
41691     typeAheadDelay : 250,
41692     /**
41693      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41694      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41695      */
41696     valueNotFoundText : undefined,
41697     /**
41698      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41699      */
41700     blockFocus : false,
41701     
41702     /**
41703      * @cfg {Boolean} disableClear Disable showing of clear button.
41704      */
41705     disableClear : false,
41706     /**
41707      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41708      */
41709     alwaysQuery : false,
41710     
41711     //private
41712     addicon : false,
41713     editicon: false,
41714     
41715     // element that contains real text value.. (when hidden is used..)
41716      
41717     // private
41718     onRender : function(ct, position)
41719     {
41720         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41721         
41722         if(this.hiddenName){
41723             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41724                     'before', true);
41725             this.hiddenField.value =
41726                 this.hiddenValue !== undefined ? this.hiddenValue :
41727                 this.value !== undefined ? this.value : '';
41728
41729             // prevent input submission
41730             this.el.dom.removeAttribute('name');
41731              
41732              
41733         }
41734         
41735         if(Roo.isGecko){
41736             this.el.dom.setAttribute('autocomplete', 'off');
41737         }
41738
41739         var cls = 'x-combo-list';
41740
41741         this.list = new Roo.Layer({
41742             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41743         });
41744
41745         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41746         this.list.setWidth(lw);
41747         this.list.swallowEvent('mousewheel');
41748         this.assetHeight = 0;
41749
41750         if(this.title){
41751             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41752             this.assetHeight += this.header.getHeight();
41753         }
41754
41755         this.innerList = this.list.createChild({cls:cls+'-inner'});
41756         this.innerList.on('mouseover', this.onViewOver, this);
41757         this.innerList.on('mousemove', this.onViewMove, this);
41758         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41759         
41760         if(this.allowBlank && !this.pageSize && !this.disableClear){
41761             this.footer = this.list.createChild({cls:cls+'-ft'});
41762             this.pageTb = new Roo.Toolbar(this.footer);
41763            
41764         }
41765         if(this.pageSize){
41766             this.footer = this.list.createChild({cls:cls+'-ft'});
41767             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41768                     {pageSize: this.pageSize});
41769             
41770         }
41771         
41772         if (this.pageTb && this.allowBlank && !this.disableClear) {
41773             var _this = this;
41774             this.pageTb.add(new Roo.Toolbar.Fill(), {
41775                 cls: 'x-btn-icon x-btn-clear',
41776                 text: '&#160;',
41777                 handler: function()
41778                 {
41779                     _this.collapse();
41780                     _this.clearValue();
41781                     _this.onSelect(false, -1);
41782                 }
41783             });
41784         }
41785         if (this.footer) {
41786             this.assetHeight += this.footer.getHeight();
41787         }
41788         
41789
41790         if(!this.tpl){
41791             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41792         }
41793
41794         this.view = new Roo.View(this.innerList, this.tpl, {
41795             singleSelect:true,
41796             store: this.store,
41797             selectedClass: this.selectedClass
41798         });
41799
41800         this.view.on('click', this.onViewClick, this);
41801
41802         this.store.on('beforeload', this.onBeforeLoad, this);
41803         this.store.on('load', this.onLoad, this);
41804         this.store.on('loadexception', this.onLoadException, this);
41805
41806         if(this.resizable){
41807             this.resizer = new Roo.Resizable(this.list,  {
41808                pinned:true, handles:'se'
41809             });
41810             this.resizer.on('resize', function(r, w, h){
41811                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41812                 this.listWidth = w;
41813                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41814                 this.restrictHeight();
41815             }, this);
41816             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41817         }
41818         if(!this.editable){
41819             this.editable = true;
41820             this.setEditable(false);
41821         }  
41822         
41823         
41824         if (typeof(this.events.add.listeners) != 'undefined') {
41825             
41826             this.addicon = this.wrap.createChild(
41827                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41828        
41829             this.addicon.on('click', function(e) {
41830                 this.fireEvent('add', this);
41831             }, this);
41832         }
41833         if (typeof(this.events.edit.listeners) != 'undefined') {
41834             
41835             this.editicon = this.wrap.createChild(
41836                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41837             if (this.addicon) {
41838                 this.editicon.setStyle('margin-left', '40px');
41839             }
41840             this.editicon.on('click', function(e) {
41841                 
41842                 // we fire even  if inothing is selected..
41843                 this.fireEvent('edit', this, this.lastData );
41844                 
41845             }, this);
41846         }
41847         
41848         
41849         
41850     },
41851
41852     // private
41853     initEvents : function(){
41854         Roo.form.ComboBox.superclass.initEvents.call(this);
41855
41856         this.keyNav = new Roo.KeyNav(this.el, {
41857             "up" : function(e){
41858                 this.inKeyMode = true;
41859                 this.selectPrev();
41860             },
41861
41862             "down" : function(e){
41863                 if(!this.isExpanded()){
41864                     this.onTriggerClick();
41865                 }else{
41866                     this.inKeyMode = true;
41867                     this.selectNext();
41868                 }
41869             },
41870
41871             "enter" : function(e){
41872                 this.onViewClick();
41873                 //return true;
41874             },
41875
41876             "esc" : function(e){
41877                 this.collapse();
41878             },
41879
41880             "tab" : function(e){
41881                 this.onViewClick(false);
41882                 this.fireEvent("specialkey", this, e);
41883                 return true;
41884             },
41885
41886             scope : this,
41887
41888             doRelay : function(foo, bar, hname){
41889                 if(hname == 'down' || this.scope.isExpanded()){
41890                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41891                 }
41892                 return true;
41893             },
41894
41895             forceKeyDown: true
41896         });
41897         this.queryDelay = Math.max(this.queryDelay || 10,
41898                 this.mode == 'local' ? 10 : 250);
41899         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41900         if(this.typeAhead){
41901             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41902         }
41903         if(this.editable !== false){
41904             this.el.on("keyup", this.onKeyUp, this);
41905         }
41906         if(this.forceSelection){
41907             this.on('blur', this.doForce, this);
41908         }
41909     },
41910
41911     onDestroy : function(){
41912         if(this.view){
41913             this.view.setStore(null);
41914             this.view.el.removeAllListeners();
41915             this.view.el.remove();
41916             this.view.purgeListeners();
41917         }
41918         if(this.list){
41919             this.list.destroy();
41920         }
41921         if(this.store){
41922             this.store.un('beforeload', this.onBeforeLoad, this);
41923             this.store.un('load', this.onLoad, this);
41924             this.store.un('loadexception', this.onLoadException, this);
41925         }
41926         Roo.form.ComboBox.superclass.onDestroy.call(this);
41927     },
41928
41929     // private
41930     fireKey : function(e){
41931         if(e.isNavKeyPress() && !this.list.isVisible()){
41932             this.fireEvent("specialkey", this, e);
41933         }
41934     },
41935
41936     // private
41937     onResize: function(w, h){
41938         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41939         
41940         if(typeof w != 'number'){
41941             // we do not handle it!?!?
41942             return;
41943         }
41944         var tw = this.trigger.getWidth();
41945         tw += this.addicon ? this.addicon.getWidth() : 0;
41946         tw += this.editicon ? this.editicon.getWidth() : 0;
41947         var x = w - tw;
41948         this.el.setWidth( this.adjustWidth('input', x));
41949             
41950         this.trigger.setStyle('left', x+'px');
41951         
41952         if(this.list && this.listWidth === undefined){
41953             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41954             this.list.setWidth(lw);
41955             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41956         }
41957         
41958     
41959         
41960     },
41961
41962     /**
41963      * Allow or prevent the user from directly editing the field text.  If false is passed,
41964      * the user will only be able to select from the items defined in the dropdown list.  This method
41965      * is the runtime equivalent of setting the 'editable' config option at config time.
41966      * @param {Boolean} value True to allow the user to directly edit the field text
41967      */
41968     setEditable : function(value){
41969         if(value == this.editable){
41970             return;
41971         }
41972         this.editable = value;
41973         if(!value){
41974             this.el.dom.setAttribute('readOnly', true);
41975             this.el.on('mousedown', this.onTriggerClick,  this);
41976             this.el.addClass('x-combo-noedit');
41977         }else{
41978             this.el.dom.setAttribute('readOnly', false);
41979             this.el.un('mousedown', this.onTriggerClick,  this);
41980             this.el.removeClass('x-combo-noedit');
41981         }
41982     },
41983
41984     // private
41985     onBeforeLoad : function(){
41986         if(!this.hasFocus){
41987             return;
41988         }
41989         this.innerList.update(this.loadingText ?
41990                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41991         this.restrictHeight();
41992         this.selectedIndex = -1;
41993     },
41994
41995     // private
41996     onLoad : function(){
41997         if(!this.hasFocus){
41998             return;
41999         }
42000         if(this.store.getCount() > 0){
42001             this.expand();
42002             this.restrictHeight();
42003             if(this.lastQuery == this.allQuery){
42004                 if(this.editable){
42005                     this.el.dom.select();
42006                 }
42007                 if(!this.selectByValue(this.value, true)){
42008                     this.select(0, true);
42009                 }
42010             }else{
42011                 this.selectNext();
42012                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42013                     this.taTask.delay(this.typeAheadDelay);
42014                 }
42015             }
42016         }else{
42017             this.onEmptyResults();
42018         }
42019         //this.el.focus();
42020     },
42021     // private
42022     onLoadException : function()
42023     {
42024         this.collapse();
42025         Roo.log(this.store.reader.jsonData);
42026         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42027             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42028         }
42029         
42030         
42031     },
42032     // private
42033     onTypeAhead : function(){
42034         if(this.store.getCount() > 0){
42035             var r = this.store.getAt(0);
42036             var newValue = r.data[this.displayField];
42037             var len = newValue.length;
42038             var selStart = this.getRawValue().length;
42039             if(selStart != len){
42040                 this.setRawValue(newValue);
42041                 this.selectText(selStart, newValue.length);
42042             }
42043         }
42044     },
42045
42046     // private
42047     onSelect : function(record, index){
42048         if(this.fireEvent('beforeselect', this, record, index) !== false){
42049             this.setFromData(index > -1 ? record.data : false);
42050             this.collapse();
42051             this.fireEvent('select', this, record, index);
42052         }
42053     },
42054
42055     /**
42056      * Returns the currently selected field value or empty string if no value is set.
42057      * @return {String} value The selected value
42058      */
42059     getValue : function(){
42060         if(this.valueField){
42061             return typeof this.value != 'undefined' ? this.value : '';
42062         }
42063         return Roo.form.ComboBox.superclass.getValue.call(this);
42064     },
42065
42066     /**
42067      * Clears any text/value currently set in the field
42068      */
42069     clearValue : function(){
42070         if(this.hiddenField){
42071             this.hiddenField.value = '';
42072         }
42073         this.value = '';
42074         this.setRawValue('');
42075         this.lastSelectionText = '';
42076         
42077     },
42078
42079     /**
42080      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42081      * will be displayed in the field.  If the value does not match the data value of an existing item,
42082      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42083      * Otherwise the field will be blank (although the value will still be set).
42084      * @param {String} value The value to match
42085      */
42086     setValue : function(v){
42087         var text = v;
42088         if(this.valueField){
42089             var r = this.findRecord(this.valueField, v);
42090             if(r){
42091                 text = r.data[this.displayField];
42092             }else if(this.valueNotFoundText !== undefined){
42093                 text = this.valueNotFoundText;
42094             }
42095         }
42096         this.lastSelectionText = text;
42097         if(this.hiddenField){
42098             this.hiddenField.value = v;
42099         }
42100         Roo.form.ComboBox.superclass.setValue.call(this, text);
42101         this.value = v;
42102     },
42103     /**
42104      * @property {Object} the last set data for the element
42105      */
42106     
42107     lastData : false,
42108     /**
42109      * Sets the value of the field based on a object which is related to the record format for the store.
42110      * @param {Object} value the value to set as. or false on reset?
42111      */
42112     setFromData : function(o){
42113         var dv = ''; // display value
42114         var vv = ''; // value value..
42115         this.lastData = o;
42116         if (this.displayField) {
42117             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42118         } else {
42119             // this is an error condition!!!
42120             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42121         }
42122         
42123         if(this.valueField){
42124             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42125         }
42126         if(this.hiddenField){
42127             this.hiddenField.value = vv;
42128             
42129             this.lastSelectionText = dv;
42130             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42131             this.value = vv;
42132             return;
42133         }
42134         // no hidden field.. - we store the value in 'value', but still display
42135         // display field!!!!
42136         this.lastSelectionText = dv;
42137         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42138         this.value = vv;
42139         
42140         
42141     },
42142     // private
42143     reset : function(){
42144         // overridden so that last data is reset..
42145         this.setValue(this.resetValue);
42146         this.originalValue = this.getValue();
42147         this.clearInvalid();
42148         this.lastData = false;
42149         if (this.view) {
42150             this.view.clearSelections();
42151         }
42152     },
42153     // private
42154     findRecord : function(prop, value){
42155         var record;
42156         if(this.store.getCount() > 0){
42157             this.store.each(function(r){
42158                 if(r.data[prop] == value){
42159                     record = r;
42160                     return false;
42161                 }
42162                 return true;
42163             });
42164         }
42165         return record;
42166     },
42167     
42168     getName: function()
42169     {
42170         // returns hidden if it's set..
42171         if (!this.rendered) {return ''};
42172         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42173         
42174     },
42175     // private
42176     onViewMove : function(e, t){
42177         this.inKeyMode = false;
42178     },
42179
42180     // private
42181     onViewOver : function(e, t){
42182         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42183             return;
42184         }
42185         var item = this.view.findItemFromChild(t);
42186         if(item){
42187             var index = this.view.indexOf(item);
42188             this.select(index, false);
42189         }
42190     },
42191
42192     // private
42193     onViewClick : function(doFocus)
42194     {
42195         var index = this.view.getSelectedIndexes()[0];
42196         var r = this.store.getAt(index);
42197         if(r){
42198             this.onSelect(r, index);
42199         }
42200         if(doFocus !== false && !this.blockFocus){
42201             this.el.focus();
42202         }
42203     },
42204
42205     // private
42206     restrictHeight : function(){
42207         this.innerList.dom.style.height = '';
42208         var inner = this.innerList.dom;
42209         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42210         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42211         this.list.beginUpdate();
42212         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42213         this.list.alignTo(this.el, this.listAlign);
42214         this.list.endUpdate();
42215     },
42216
42217     // private
42218     onEmptyResults : function(){
42219         this.collapse();
42220     },
42221
42222     /**
42223      * Returns true if the dropdown list is expanded, else false.
42224      */
42225     isExpanded : function(){
42226         return this.list.isVisible();
42227     },
42228
42229     /**
42230      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42231      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42232      * @param {String} value The data value of the item to select
42233      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42234      * selected item if it is not currently in view (defaults to true)
42235      * @return {Boolean} True if the value matched an item in the list, else false
42236      */
42237     selectByValue : function(v, scrollIntoView){
42238         if(v !== undefined && v !== null){
42239             var r = this.findRecord(this.valueField || this.displayField, v);
42240             if(r){
42241                 this.select(this.store.indexOf(r), scrollIntoView);
42242                 return true;
42243             }
42244         }
42245         return false;
42246     },
42247
42248     /**
42249      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42250      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42251      * @param {Number} index The zero-based index of the list item to select
42252      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42253      * selected item if it is not currently in view (defaults to true)
42254      */
42255     select : function(index, scrollIntoView){
42256         this.selectedIndex = index;
42257         this.view.select(index);
42258         if(scrollIntoView !== false){
42259             var el = this.view.getNode(index);
42260             if(el){
42261                 this.innerList.scrollChildIntoView(el, false);
42262             }
42263         }
42264     },
42265
42266     // private
42267     selectNext : function(){
42268         var ct = this.store.getCount();
42269         if(ct > 0){
42270             if(this.selectedIndex == -1){
42271                 this.select(0);
42272             }else if(this.selectedIndex < ct-1){
42273                 this.select(this.selectedIndex+1);
42274             }
42275         }
42276     },
42277
42278     // private
42279     selectPrev : function(){
42280         var ct = this.store.getCount();
42281         if(ct > 0){
42282             if(this.selectedIndex == -1){
42283                 this.select(0);
42284             }else if(this.selectedIndex != 0){
42285                 this.select(this.selectedIndex-1);
42286             }
42287         }
42288     },
42289
42290     // private
42291     onKeyUp : function(e){
42292         if(this.editable !== false && !e.isSpecialKey()){
42293             this.lastKey = e.getKey();
42294             this.dqTask.delay(this.queryDelay);
42295         }
42296     },
42297
42298     // private
42299     validateBlur : function(){
42300         return !this.list || !this.list.isVisible();   
42301     },
42302
42303     // private
42304     initQuery : function(){
42305         this.doQuery(this.getRawValue());
42306     },
42307
42308     // private
42309     doForce : function(){
42310         if(this.el.dom.value.length > 0){
42311             this.el.dom.value =
42312                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42313              
42314         }
42315     },
42316
42317     /**
42318      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42319      * query allowing the query action to be canceled if needed.
42320      * @param {String} query The SQL query to execute
42321      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42322      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42323      * saved in the current store (defaults to false)
42324      */
42325     doQuery : function(q, forceAll){
42326         if(q === undefined || q === null){
42327             q = '';
42328         }
42329         var qe = {
42330             query: q,
42331             forceAll: forceAll,
42332             combo: this,
42333             cancel:false
42334         };
42335         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42336             return false;
42337         }
42338         q = qe.query;
42339         forceAll = qe.forceAll;
42340         if(forceAll === true || (q.length >= this.minChars)){
42341             if(this.lastQuery != q || this.alwaysQuery){
42342                 this.lastQuery = q;
42343                 if(this.mode == 'local'){
42344                     this.selectedIndex = -1;
42345                     if(forceAll){
42346                         this.store.clearFilter();
42347                     }else{
42348                         this.store.filter(this.displayField, q);
42349                     }
42350                     this.onLoad();
42351                 }else{
42352                     this.store.baseParams[this.queryParam] = q;
42353                     this.store.load({
42354                         params: this.getParams(q)
42355                     });
42356                     this.expand();
42357                 }
42358             }else{
42359                 this.selectedIndex = -1;
42360                 this.onLoad();   
42361             }
42362         }
42363     },
42364
42365     // private
42366     getParams : function(q){
42367         var p = {};
42368         //p[this.queryParam] = q;
42369         if(this.pageSize){
42370             p.start = 0;
42371             p.limit = this.pageSize;
42372         }
42373         return p;
42374     },
42375
42376     /**
42377      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42378      */
42379     collapse : function(){
42380         if(!this.isExpanded()){
42381             return;
42382         }
42383         this.list.hide();
42384         Roo.get(document).un('mousedown', this.collapseIf, this);
42385         Roo.get(document).un('mousewheel', this.collapseIf, this);
42386         if (!this.editable) {
42387             Roo.get(document).un('keydown', this.listKeyPress, this);
42388         }
42389         this.fireEvent('collapse', this);
42390     },
42391
42392     // private
42393     collapseIf : function(e){
42394         if(!e.within(this.wrap) && !e.within(this.list)){
42395             this.collapse();
42396         }
42397     },
42398
42399     /**
42400      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42401      */
42402     expand : function(){
42403         if(this.isExpanded() || !this.hasFocus){
42404             return;
42405         }
42406         this.list.alignTo(this.el, this.listAlign);
42407         this.list.show();
42408         Roo.get(document).on('mousedown', this.collapseIf, this);
42409         Roo.get(document).on('mousewheel', this.collapseIf, this);
42410         if (!this.editable) {
42411             Roo.get(document).on('keydown', this.listKeyPress, this);
42412         }
42413         
42414         this.fireEvent('expand', this);
42415     },
42416
42417     // private
42418     // Implements the default empty TriggerField.onTriggerClick function
42419     onTriggerClick : function(){
42420         if(this.disabled){
42421             return;
42422         }
42423         if(this.isExpanded()){
42424             this.collapse();
42425             if (!this.blockFocus) {
42426                 this.el.focus();
42427             }
42428             
42429         }else {
42430             this.hasFocus = true;
42431             if(this.triggerAction == 'all') {
42432                 this.doQuery(this.allQuery, true);
42433             } else {
42434                 this.doQuery(this.getRawValue());
42435             }
42436             if (!this.blockFocus) {
42437                 this.el.focus();
42438             }
42439         }
42440     },
42441     listKeyPress : function(e)
42442     {
42443         //Roo.log('listkeypress');
42444         // scroll to first matching element based on key pres..
42445         if (e.isSpecialKey()) {
42446             return false;
42447         }
42448         var k = String.fromCharCode(e.getKey()).toUpperCase();
42449         //Roo.log(k);
42450         var match  = false;
42451         var csel = this.view.getSelectedNodes();
42452         var cselitem = false;
42453         if (csel.length) {
42454             var ix = this.view.indexOf(csel[0]);
42455             cselitem  = this.store.getAt(ix);
42456             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42457                 cselitem = false;
42458             }
42459             
42460         }
42461         
42462         this.store.each(function(v) { 
42463             if (cselitem) {
42464                 // start at existing selection.
42465                 if (cselitem.id == v.id) {
42466                     cselitem = false;
42467                 }
42468                 return;
42469             }
42470                 
42471             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42472                 match = this.store.indexOf(v);
42473                 return false;
42474             }
42475         }, this);
42476         
42477         if (match === false) {
42478             return true; // no more action?
42479         }
42480         // scroll to?
42481         this.view.select(match);
42482         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42483         sn.scrollIntoView(sn.dom.parentNode, false);
42484     } 
42485
42486     /** 
42487     * @cfg {Boolean} grow 
42488     * @hide 
42489     */
42490     /** 
42491     * @cfg {Number} growMin 
42492     * @hide 
42493     */
42494     /** 
42495     * @cfg {Number} growMax 
42496     * @hide 
42497     */
42498     /**
42499      * @hide
42500      * @method autoSize
42501      */
42502 });/*
42503  * Copyright(c) 2010-2012, Roo J Solutions Limited
42504  *
42505  * Licence LGPL
42506  *
42507  */
42508
42509 /**
42510  * @class Roo.form.ComboBoxArray
42511  * @extends Roo.form.TextField
42512  * A facebook style adder... for lists of email / people / countries  etc...
42513  * pick multiple items from a combo box, and shows each one.
42514  *
42515  *  Fred [x]  Brian [x]  [Pick another |v]
42516  *
42517  *
42518  *  For this to work: it needs various extra information
42519  *    - normal combo problay has
42520  *      name, hiddenName
42521  *    + displayField, valueField
42522  *
42523  *    For our purpose...
42524  *
42525  *
42526  *   If we change from 'extends' to wrapping...
42527  *   
42528  *  
42529  *
42530  
42531  
42532  * @constructor
42533  * Create a new ComboBoxArray.
42534  * @param {Object} config Configuration options
42535  */
42536  
42537
42538 Roo.form.ComboBoxArray = function(config)
42539 {
42540     this.addEvents({
42541         /**
42542          * @event beforeremove
42543          * Fires before remove the value from the list
42544              * @param {Roo.form.ComboBoxArray} _self This combo box array
42545              * @param {Roo.form.ComboBoxArray.Item} item removed item
42546              */
42547         'beforeremove' : true,
42548         /**
42549          * @event remove
42550          * Fires when remove the value from the list
42551              * @param {Roo.form.ComboBoxArray} _self This combo box array
42552              * @param {Roo.form.ComboBoxArray.Item} item removed item
42553              */
42554         'remove' : true
42555         
42556         
42557     });
42558     
42559     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42560     
42561     this.items = new Roo.util.MixedCollection(false);
42562     
42563     // construct the child combo...
42564     
42565     
42566     
42567     
42568    
42569     
42570 }
42571
42572  
42573 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42574
42575     /**
42576      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
42577      */
42578     
42579     lastData : false,
42580     
42581     // behavies liek a hiddne field
42582     inputType:      'hidden',
42583     /**
42584      * @cfg {Number} width The width of the box that displays the selected element
42585      */ 
42586     width:          300,
42587
42588     
42589     
42590     /**
42591      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42592      */
42593     name : false,
42594     /**
42595      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42596      */
42597     hiddenName : false,
42598       /**
42599      * @cfg {String} seperator    The value seperator normally ',' 
42600      */
42601     seperator : ',',
42602     
42603     // private the array of items that are displayed..
42604     items  : false,
42605     // private - the hidden field el.
42606     hiddenEl : false,
42607     // private - the filed el..
42608     el : false,
42609     
42610     //validateValue : function() { return true; }, // all values are ok!
42611     //onAddClick: function() { },
42612     
42613     onRender : function(ct, position) 
42614     {
42615         
42616         // create the standard hidden element
42617         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42618         
42619         
42620         // give fake names to child combo;
42621         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42622         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42623         
42624         this.combo = Roo.factory(this.combo, Roo.form);
42625         this.combo.onRender(ct, position);
42626         if (typeof(this.combo.width) != 'undefined') {
42627             this.combo.onResize(this.combo.width,0);
42628         }
42629         
42630         this.combo.initEvents();
42631         
42632         // assigned so form know we need to do this..
42633         this.store          = this.combo.store;
42634         this.valueField     = this.combo.valueField;
42635         this.displayField   = this.combo.displayField ;
42636         
42637         
42638         this.combo.wrap.addClass('x-cbarray-grp');
42639         
42640         var cbwrap = this.combo.wrap.createChild(
42641             {tag: 'div', cls: 'x-cbarray-cb'},
42642             this.combo.el.dom
42643         );
42644         
42645              
42646         this.hiddenEl = this.combo.wrap.createChild({
42647             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42648         });
42649         this.el = this.combo.wrap.createChild({
42650             tag: 'input',  type:'hidden' , name: this.name, value : ''
42651         });
42652          //   this.el.dom.removeAttribute("name");
42653         
42654         
42655         this.outerWrap = this.combo.wrap;
42656         this.wrap = cbwrap;
42657         
42658         this.outerWrap.setWidth(this.width);
42659         this.outerWrap.dom.removeChild(this.el.dom);
42660         
42661         this.wrap.dom.appendChild(this.el.dom);
42662         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42663         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42664         
42665         this.combo.trigger.setStyle('position','relative');
42666         this.combo.trigger.setStyle('left', '0px');
42667         this.combo.trigger.setStyle('top', '2px');
42668         
42669         this.combo.el.setStyle('vertical-align', 'text-bottom');
42670         
42671         //this.trigger.setStyle('vertical-align', 'top');
42672         
42673         // this should use the code from combo really... on('add' ....)
42674         if (this.adder) {
42675             
42676         
42677             this.adder = this.outerWrap.createChild(
42678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42679             var _t = this;
42680             this.adder.on('click', function(e) {
42681                 _t.fireEvent('adderclick', this, e);
42682             }, _t);
42683         }
42684         //var _t = this;
42685         //this.adder.on('click', this.onAddClick, _t);
42686         
42687         
42688         this.combo.on('select', function(cb, rec, ix) {
42689             this.addItem(rec.data);
42690             
42691             cb.setValue('');
42692             cb.el.dom.value = '';
42693             //cb.lastData = rec.data;
42694             // add to list
42695             
42696         }, this);
42697         
42698         
42699     },
42700     
42701     
42702     getName: function()
42703     {
42704         // returns hidden if it's set..
42705         if (!this.rendered) {return ''};
42706         return  this.hiddenName ? this.hiddenName : this.name;
42707         
42708     },
42709     
42710     
42711     onResize: function(w, h){
42712         
42713         return;
42714         // not sure if this is needed..
42715         //this.combo.onResize(w,h);
42716         
42717         if(typeof w != 'number'){
42718             // we do not handle it!?!?
42719             return;
42720         }
42721         var tw = this.combo.trigger.getWidth();
42722         tw += this.addicon ? this.addicon.getWidth() : 0;
42723         tw += this.editicon ? this.editicon.getWidth() : 0;
42724         var x = w - tw;
42725         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42726             
42727         this.combo.trigger.setStyle('left', '0px');
42728         
42729         if(this.list && this.listWidth === undefined){
42730             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42731             this.list.setWidth(lw);
42732             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42733         }
42734         
42735     
42736         
42737     },
42738     
42739     addItem: function(rec)
42740     {
42741         var valueField = this.combo.valueField;
42742         var displayField = this.combo.displayField;
42743         
42744         if (this.items.indexOfKey(rec[valueField]) > -1) {
42745             //console.log("GOT " + rec.data.id);
42746             return;
42747         }
42748         
42749         var x = new Roo.form.ComboBoxArray.Item({
42750             //id : rec[this.idField],
42751             data : rec,
42752             displayField : displayField ,
42753             tipField : displayField ,
42754             cb : this
42755         });
42756         // use the 
42757         this.items.add(rec[valueField],x);
42758         // add it before the element..
42759         this.updateHiddenEl();
42760         x.render(this.outerWrap, this.wrap.dom);
42761         // add the image handler..
42762     },
42763     
42764     updateHiddenEl : function()
42765     {
42766         this.validate();
42767         if (!this.hiddenEl) {
42768             return;
42769         }
42770         var ar = [];
42771         var idField = this.combo.valueField;
42772         
42773         this.items.each(function(f) {
42774             ar.push(f.data[idField]);
42775         });
42776         this.hiddenEl.dom.value = ar.join(this.seperator);
42777         this.validate();
42778     },
42779     
42780     reset : function()
42781     {
42782         this.items.clear();
42783         
42784         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42785            el.remove();
42786         });
42787         
42788         this.el.dom.value = '';
42789         if (this.hiddenEl) {
42790             this.hiddenEl.dom.value = '';
42791         }
42792         
42793     },
42794     getValue: function()
42795     {
42796         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42797     },
42798     setValue: function(v) // not a valid action - must use addItems..
42799     {
42800         
42801         this.reset();
42802          
42803         if (this.store.isLocal && (typeof(v) == 'string')) {
42804             // then we can use the store to find the values..
42805             // comma seperated at present.. this needs to allow JSON based encoding..
42806             this.hiddenEl.value  = v;
42807             var v_ar = [];
42808             Roo.each(v.split(this.seperator), function(k) {
42809                 Roo.log("CHECK " + this.valueField + ',' + k);
42810                 var li = this.store.query(this.valueField, k);
42811                 if (!li.length) {
42812                     return;
42813                 }
42814                 var add = {};
42815                 add[this.valueField] = k;
42816                 add[this.displayField] = li.item(0).data[this.displayField];
42817                 
42818                 this.addItem(add);
42819             }, this) 
42820              
42821         }
42822         if (typeof(v) == 'object' ) {
42823             // then let's assume it's an array of objects..
42824             Roo.each(v, function(l) {
42825                 var add = l;
42826                 if (typeof(l) == 'string') {
42827                     add = {};
42828                     add[this.valueField] = l;
42829                     add[this.displayField] = l
42830                 }
42831                 this.addItem(add);
42832             }, this);
42833              
42834         }
42835         
42836         
42837     },
42838     setFromData: function(v)
42839     {
42840         // this recieves an object, if setValues is called.
42841         this.reset();
42842         this.el.dom.value = v[this.displayField];
42843         this.hiddenEl.dom.value = v[this.valueField];
42844         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42845             return;
42846         }
42847         var kv = v[this.valueField];
42848         var dv = v[this.displayField];
42849         kv = typeof(kv) != 'string' ? '' : kv;
42850         dv = typeof(dv) != 'string' ? '' : dv;
42851         
42852         
42853         var keys = kv.split(this.seperator);
42854         var display = dv.split(this.seperator);
42855         for (var i = 0 ; i < keys.length; i++) {
42856             add = {};
42857             add[this.valueField] = keys[i];
42858             add[this.displayField] = display[i];
42859             this.addItem(add);
42860         }
42861       
42862         
42863     },
42864     
42865     /**
42866      * Validates the combox array value
42867      * @return {Boolean} True if the value is valid, else false
42868      */
42869     validate : function(){
42870         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42871             this.clearInvalid();
42872             return true;
42873         }
42874         return false;
42875     },
42876     
42877     validateValue : function(value){
42878         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42879         
42880     },
42881     
42882     /*@
42883      * overide
42884      * 
42885      */
42886     isDirty : function() {
42887         if(this.disabled) {
42888             return false;
42889         }
42890         
42891         try {
42892             var d = Roo.decode(String(this.originalValue));
42893         } catch (e) {
42894             return String(this.getValue()) !== String(this.originalValue);
42895         }
42896         
42897         var originalValue = [];
42898         
42899         for (var i = 0; i < d.length; i++){
42900             originalValue.push(d[i][this.valueField]);
42901         }
42902         
42903         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42904         
42905     }
42906     
42907 });
42908
42909
42910
42911 /**
42912  * @class Roo.form.ComboBoxArray.Item
42913  * @extends Roo.BoxComponent
42914  * A selected item in the list
42915  *  Fred [x]  Brian [x]  [Pick another |v]
42916  * 
42917  * @constructor
42918  * Create a new item.
42919  * @param {Object} config Configuration options
42920  */
42921  
42922 Roo.form.ComboBoxArray.Item = function(config) {
42923     config.id = Roo.id();
42924     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42925 }
42926
42927 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42928     data : {},
42929     cb: false,
42930     displayField : false,
42931     tipField : false,
42932     
42933     
42934     defaultAutoCreate : {
42935         tag: 'div',
42936         cls: 'x-cbarray-item',
42937         cn : [ 
42938             { tag: 'div' },
42939             {
42940                 tag: 'img',
42941                 width:16,
42942                 height : 16,
42943                 src : Roo.BLANK_IMAGE_URL ,
42944                 align: 'center'
42945             }
42946         ]
42947         
42948     },
42949     
42950  
42951     onRender : function(ct, position)
42952     {
42953         Roo.form.Field.superclass.onRender.call(this, ct, position);
42954         
42955         if(!this.el){
42956             var cfg = this.getAutoCreate();
42957             this.el = ct.createChild(cfg, position);
42958         }
42959         
42960         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42961         
42962         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42963             this.cb.renderer(this.data) :
42964             String.format('{0}',this.data[this.displayField]);
42965         
42966             
42967         this.el.child('div').dom.setAttribute('qtip',
42968                         String.format('{0}',this.data[this.tipField])
42969         );
42970         
42971         this.el.child('img').on('click', this.remove, this);
42972         
42973     },
42974    
42975     remove : function()
42976     {
42977         if(this.cb.disabled){
42978             return;
42979         }
42980         
42981         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42982             this.cb.items.remove(this);
42983             this.el.child('img').un('click', this.remove, this);
42984             this.el.remove();
42985             this.cb.updateHiddenEl();
42986
42987             this.cb.fireEvent('remove', this.cb, this);
42988         }
42989         
42990     }
42991 });/*
42992  * RooJS Library 1.1.1
42993  * Copyright(c) 2008-2011  Alan Knowles
42994  *
42995  * License - LGPL
42996  */
42997  
42998
42999 /**
43000  * @class Roo.form.ComboNested
43001  * @extends Roo.form.ComboBox
43002  * A combobox for that allows selection of nested items in a list,
43003  * eg.
43004  *
43005  *  Book
43006  *    -> red
43007  *    -> green
43008  *  Table
43009  *    -> square
43010  *      ->red
43011  *      ->green
43012  *    -> rectangle
43013  *      ->green
43014  *      
43015  * 
43016  * @constructor
43017  * Create a new ComboNested
43018  * @param {Object} config Configuration options
43019  */
43020 Roo.form.ComboNested = function(config){
43021     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43022     // should verify some data...
43023     // like
43024     // hiddenName = required..
43025     // displayField = required
43026     // valudField == required
43027     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43028     var _t = this;
43029     Roo.each(req, function(e) {
43030         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43031             throw "Roo.form.ComboNested : missing value for: " + e;
43032         }
43033     });
43034      
43035     
43036 };
43037
43038 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43039    
43040     /*
43041      * @config {Number} max Number of columns to show
43042      */
43043     
43044     maxColumns : 3,
43045    
43046     list : null, // the outermost div..
43047     innerLists : null, // the
43048     views : null,
43049     stores : null,
43050     // private
43051     loadingChildren : false,
43052     
43053     onRender : function(ct, position)
43054     {
43055         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43056         
43057         if(this.hiddenName){
43058             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43059                     'before', true);
43060             this.hiddenField.value =
43061                 this.hiddenValue !== undefined ? this.hiddenValue :
43062                 this.value !== undefined ? this.value : '';
43063
43064             // prevent input submission
43065             this.el.dom.removeAttribute('name');
43066              
43067              
43068         }
43069         
43070         if(Roo.isGecko){
43071             this.el.dom.setAttribute('autocomplete', 'off');
43072         }
43073
43074         var cls = 'x-combo-list';
43075
43076         this.list = new Roo.Layer({
43077             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43078         });
43079
43080         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43081         this.list.setWidth(lw);
43082         this.list.swallowEvent('mousewheel');
43083         this.assetHeight = 0;
43084
43085         if(this.title){
43086             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43087             this.assetHeight += this.header.getHeight();
43088         }
43089         this.innerLists = [];
43090         this.views = [];
43091         this.stores = [];
43092         for (var i =0 ; i < this.maxColumns; i++) {
43093             this.onRenderList( cls, i);
43094         }
43095         
43096         // always needs footer, as we are going to have an 'OK' button.
43097         this.footer = this.list.createChild({cls:cls+'-ft'});
43098         this.pageTb = new Roo.Toolbar(this.footer);  
43099         var _this = this;
43100         this.pageTb.add(  {
43101             
43102             text: 'Done',
43103             handler: function()
43104             {
43105                 _this.collapse();
43106             }
43107         });
43108         
43109         if ( this.allowBlank && !this.disableClear) {
43110             
43111             this.pageTb.add(new Roo.Toolbar.Fill(), {
43112                 cls: 'x-btn-icon x-btn-clear',
43113                 text: '&#160;',
43114                 handler: function()
43115                 {
43116                     _this.collapse();
43117                     _this.clearValue();
43118                     _this.onSelect(false, -1);
43119                 }
43120             });
43121         }
43122         if (this.footer) {
43123             this.assetHeight += this.footer.getHeight();
43124         }
43125         
43126     },
43127     onRenderList : function (  cls, i)
43128     {
43129         
43130         var lw = Math.floor(
43131                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43132         );
43133         
43134         this.list.setWidth(lw); // default to '1'
43135
43136         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43137         //il.on('mouseover', this.onViewOver, this, { list:  i });
43138         //il.on('mousemove', this.onViewMove, this, { list:  i });
43139         il.setWidth(lw);
43140         il.setStyle({ 'overflow-x' : 'hidden'});
43141
43142         if(!this.tpl){
43143             this.tpl = new Roo.Template({
43144                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43145                 isEmpty: function (value, allValues) {
43146                     //Roo.log(value);
43147                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43148                     return dl ? 'has-children' : 'no-children'
43149                 }
43150             });
43151         }
43152         
43153         var store  = this.store;
43154         if (i > 0) {
43155             store  = new Roo.data.SimpleStore({
43156                 //fields : this.store.reader.meta.fields,
43157                 reader : this.store.reader,
43158                 data : [ ]
43159             });
43160         }
43161         this.stores[i]  = store;
43162                   
43163         var view = this.views[i] = new Roo.View(
43164             il,
43165             this.tpl,
43166             {
43167                 singleSelect:true,
43168                 store: store,
43169                 selectedClass: this.selectedClass
43170             }
43171         );
43172         view.getEl().setWidth(lw);
43173         view.getEl().setStyle({
43174             position: i < 1 ? 'relative' : 'absolute',
43175             top: 0,
43176             left: (i * lw ) + 'px',
43177             display : i > 0 ? 'none' : 'block'
43178         });
43179         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43180         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43181         //view.on('click', this.onViewClick, this, { list : i });
43182
43183         store.on('beforeload', this.onBeforeLoad, this);
43184         store.on('load',  this.onLoad, this, { list  : i});
43185         store.on('loadexception', this.onLoadException, this);
43186
43187         // hide the other vies..
43188         
43189         
43190         
43191     },
43192       
43193     restrictHeight : function()
43194     {
43195         var mh = 0;
43196         Roo.each(this.innerLists, function(il,i) {
43197             var el = this.views[i].getEl();
43198             el.dom.style.height = '';
43199             var inner = el.dom;
43200             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43201             // only adjust heights on other ones..
43202             mh = Math.max(h, mh);
43203             if (i < 1) {
43204                 
43205                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43206                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43207                
43208             }
43209             
43210             
43211         }, this);
43212         
43213         this.list.beginUpdate();
43214         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43215         this.list.alignTo(this.el, this.listAlign);
43216         this.list.endUpdate();
43217         
43218     },
43219      
43220     
43221     // -- store handlers..
43222     // private
43223     onBeforeLoad : function()
43224     {
43225         if(!this.hasFocus){
43226             return;
43227         }
43228         this.innerLists[0].update(this.loadingText ?
43229                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43230         this.restrictHeight();
43231         this.selectedIndex = -1;
43232     },
43233     // private
43234     onLoad : function(a,b,c,d)
43235     {
43236         if (!this.loadingChildren) {
43237             // then we are loading the top level. - hide the children
43238             for (var i = 1;i < this.views.length; i++) {
43239                 this.views[i].getEl().setStyle({ display : 'none' });
43240             }
43241             var lw = Math.floor(
43242                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43243             );
43244         
43245              this.list.setWidth(lw); // default to '1'
43246
43247             
43248         }
43249         if(!this.hasFocus){
43250             return;
43251         }
43252         
43253         if(this.store.getCount() > 0) {
43254             this.expand();
43255             this.restrictHeight();   
43256         } else {
43257             this.onEmptyResults();
43258         }
43259         
43260         if (!this.loadingChildren) {
43261             this.selectActive();
43262         }
43263         /*
43264         this.stores[1].loadData([]);
43265         this.stores[2].loadData([]);
43266         this.views
43267         */    
43268     
43269         //this.el.focus();
43270     },
43271     
43272     
43273     // private
43274     onLoadException : function()
43275     {
43276         this.collapse();
43277         Roo.log(this.store.reader.jsonData);
43278         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43279             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43280         }
43281         
43282         
43283     },
43284     // no cleaning of leading spaces on blur here.
43285     cleanLeadingSpace : function(e) { },
43286     
43287
43288     onSelectChange : function (view, sels, opts )
43289     {
43290         var ix = view.getSelectedIndexes();
43291          
43292         if (opts.list > this.maxColumns - 2) {
43293             if (view.store.getCount()<  1) {
43294                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43295
43296             } else  {
43297                 if (ix.length) {
43298                     // used to clear ?? but if we are loading unselected 
43299                     this.setFromData(view.store.getAt(ix[0]).data);
43300                 }
43301                 
43302             }
43303             
43304             return;
43305         }
43306         
43307         if (!ix.length) {
43308             // this get's fired when trigger opens..
43309            // this.setFromData({});
43310             var str = this.stores[opts.list+1];
43311             str.data.clear(); // removeall wihtout the fire events..
43312             return;
43313         }
43314         
43315         var rec = view.store.getAt(ix[0]);
43316          
43317         this.setFromData(rec.data);
43318         this.fireEvent('select', this, rec, ix[0]);
43319         
43320         var lw = Math.floor(
43321              (
43322                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43323              ) / this.maxColumns
43324         );
43325         this.loadingChildren = true;
43326         this.stores[opts.list+1].loadDataFromChildren( rec );
43327         this.loadingChildren = false;
43328         var dl = this.stores[opts.list+1]. getTotalCount();
43329         
43330         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43331         
43332         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43333         for (var i = opts.list+2; i < this.views.length;i++) {
43334             this.views[i].getEl().setStyle({ display : 'none' });
43335         }
43336         
43337         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43338         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43339         
43340         if (this.isLoading) {
43341            // this.selectActive(opts.list);
43342         }
43343          
43344     },
43345     
43346     
43347     
43348     
43349     onDoubleClick : function()
43350     {
43351         this.collapse(); //??
43352     },
43353     
43354      
43355     
43356     
43357     
43358     // private
43359     recordToStack : function(store, prop, value, stack)
43360     {
43361         var cstore = new Roo.data.SimpleStore({
43362             //fields : this.store.reader.meta.fields, // we need array reader.. for
43363             reader : this.store.reader,
43364             data : [ ]
43365         });
43366         var _this = this;
43367         var record  = false;
43368         var srec = false;
43369         if(store.getCount() < 1){
43370             return false;
43371         }
43372         store.each(function(r){
43373             if(r.data[prop] == value){
43374                 record = r;
43375             srec = r;
43376                 return false;
43377             }
43378             if (r.data.cn && r.data.cn.length) {
43379                 cstore.loadDataFromChildren( r);
43380                 var cret = _this.recordToStack(cstore, prop, value, stack);
43381                 if (cret !== false) {
43382                     record = cret;
43383                     srec = r;
43384                     return false;
43385                 }
43386             }
43387              
43388             return true;
43389         });
43390         if (record == false) {
43391             return false
43392         }
43393         stack.unshift(srec);
43394         return record;
43395     },
43396     
43397     /*
43398      * find the stack of stores that match our value.
43399      *
43400      * 
43401      */
43402     
43403     selectActive : function ()
43404     {
43405         // if store is not loaded, then we will need to wait for that to happen first.
43406         var stack = [];
43407         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43408         for (var i = 0; i < stack.length; i++ ) {
43409             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43410         }
43411         
43412     }
43413         
43414          
43415     
43416     
43417     
43418     
43419 });/*
43420  * Based on:
43421  * Ext JS Library 1.1.1
43422  * Copyright(c) 2006-2007, Ext JS, LLC.
43423  *
43424  * Originally Released Under LGPL - original licence link has changed is not relivant.
43425  *
43426  * Fork - LGPL
43427  * <script type="text/javascript">
43428  */
43429 /**
43430  * @class Roo.form.Checkbox
43431  * @extends Roo.form.Field
43432  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43433  * @constructor
43434  * Creates a new Checkbox
43435  * @param {Object} config Configuration options
43436  */
43437 Roo.form.Checkbox = function(config){
43438     Roo.form.Checkbox.superclass.constructor.call(this, config);
43439     this.addEvents({
43440         /**
43441          * @event check
43442          * Fires when the checkbox is checked or unchecked.
43443              * @param {Roo.form.Checkbox} this This checkbox
43444              * @param {Boolean} checked The new checked value
43445              */
43446         check : true
43447     });
43448 };
43449
43450 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43451     /**
43452      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43453      */
43454     focusClass : undefined,
43455     /**
43456      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43457      */
43458     fieldClass: "x-form-field",
43459     /**
43460      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43461      */
43462     checked: false,
43463     /**
43464      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43465      * {tag: "input", type: "checkbox", autocomplete: "off"})
43466      */
43467     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43468     /**
43469      * @cfg {String} boxLabel The text that appears beside the checkbox
43470      */
43471     boxLabel : "",
43472     /**
43473      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43474      */  
43475     inputValue : '1',
43476     /**
43477      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43478      */
43479      valueOff: '0', // value when not checked..
43480
43481     actionMode : 'viewEl', 
43482     //
43483     // private
43484     itemCls : 'x-menu-check-item x-form-item',
43485     groupClass : 'x-menu-group-item',
43486     inputType : 'hidden',
43487     
43488     
43489     inSetChecked: false, // check that we are not calling self...
43490     
43491     inputElement: false, // real input element?
43492     basedOn: false, // ????
43493     
43494     isFormField: true, // not sure where this is needed!!!!
43495
43496     onResize : function(){
43497         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43498         if(!this.boxLabel){
43499             this.el.alignTo(this.wrap, 'c-c');
43500         }
43501     },
43502
43503     initEvents : function(){
43504         Roo.form.Checkbox.superclass.initEvents.call(this);
43505         this.el.on("click", this.onClick,  this);
43506         this.el.on("change", this.onClick,  this);
43507     },
43508
43509
43510     getResizeEl : function(){
43511         return this.wrap;
43512     },
43513
43514     getPositionEl : function(){
43515         return this.wrap;
43516     },
43517
43518     // private
43519     onRender : function(ct, position){
43520         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43521         /*
43522         if(this.inputValue !== undefined){
43523             this.el.dom.value = this.inputValue;
43524         }
43525         */
43526         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43527         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43528         var viewEl = this.wrap.createChild({ 
43529             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43530         this.viewEl = viewEl;   
43531         this.wrap.on('click', this.onClick,  this); 
43532         
43533         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43534         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43535         
43536         
43537         
43538         if(this.boxLabel){
43539             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43540         //    viewEl.on('click', this.onClick,  this); 
43541         }
43542         //if(this.checked){
43543             this.setChecked(this.checked);
43544         //}else{
43545             //this.checked = this.el.dom;
43546         //}
43547
43548     },
43549
43550     // private
43551     initValue : Roo.emptyFn,
43552
43553     /**
43554      * Returns the checked state of the checkbox.
43555      * @return {Boolean} True if checked, else false
43556      */
43557     getValue : function(){
43558         if(this.el){
43559             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43560         }
43561         return this.valueOff;
43562         
43563     },
43564
43565         // private
43566     onClick : function(){ 
43567         if (this.disabled) {
43568             return;
43569         }
43570         this.setChecked(!this.checked);
43571
43572         //if(this.el.dom.checked != this.checked){
43573         //    this.setValue(this.el.dom.checked);
43574        // }
43575     },
43576
43577     /**
43578      * Sets the checked state of the checkbox.
43579      * On is always based on a string comparison between inputValue and the param.
43580      * @param {Boolean/String} value - the value to set 
43581      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43582      */
43583     setValue : function(v,suppressEvent){
43584         
43585         
43586         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43587         //if(this.el && this.el.dom){
43588         //    this.el.dom.checked = this.checked;
43589         //    this.el.dom.defaultChecked = this.checked;
43590         //}
43591         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43592         //this.fireEvent("check", this, this.checked);
43593     },
43594     // private..
43595     setChecked : function(state,suppressEvent)
43596     {
43597         if (this.inSetChecked) {
43598             this.checked = state;
43599             return;
43600         }
43601         
43602     
43603         if(this.wrap){
43604             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43605         }
43606         this.checked = state;
43607         if(suppressEvent !== true){
43608             this.fireEvent('check', this, state);
43609         }
43610         this.inSetChecked = true;
43611         this.el.dom.value = state ? this.inputValue : this.valueOff;
43612         this.inSetChecked = false;
43613         
43614     },
43615     // handle setting of hidden value by some other method!!?!?
43616     setFromHidden: function()
43617     {
43618         if(!this.el){
43619             return;
43620         }
43621         //console.log("SET FROM HIDDEN");
43622         //alert('setFrom hidden');
43623         this.setValue(this.el.dom.value);
43624     },
43625     
43626     onDestroy : function()
43627     {
43628         if(this.viewEl){
43629             Roo.get(this.viewEl).remove();
43630         }
43631          
43632         Roo.form.Checkbox.superclass.onDestroy.call(this);
43633     },
43634     
43635     setBoxLabel : function(str)
43636     {
43637         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43638     }
43639
43640 });/*
43641  * Based on:
43642  * Ext JS Library 1.1.1
43643  * Copyright(c) 2006-2007, Ext JS, LLC.
43644  *
43645  * Originally Released Under LGPL - original licence link has changed is not relivant.
43646  *
43647  * Fork - LGPL
43648  * <script type="text/javascript">
43649  */
43650  
43651 /**
43652  * @class Roo.form.Radio
43653  * @extends Roo.form.Checkbox
43654  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43655  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43656  * @constructor
43657  * Creates a new Radio
43658  * @param {Object} config Configuration options
43659  */
43660 Roo.form.Radio = function(){
43661     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43662 };
43663 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43664     inputType: 'radio',
43665
43666     /**
43667      * If this radio is part of a group, it will return the selected value
43668      * @return {String}
43669      */
43670     getGroupValue : function(){
43671         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43672     },
43673     
43674     
43675     onRender : function(ct, position){
43676         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43677         
43678         if(this.inputValue !== undefined){
43679             this.el.dom.value = this.inputValue;
43680         }
43681          
43682         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43683         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43684         //var viewEl = this.wrap.createChild({ 
43685         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43686         //this.viewEl = viewEl;   
43687         //this.wrap.on('click', this.onClick,  this); 
43688         
43689         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43690         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43691         
43692         
43693         
43694         if(this.boxLabel){
43695             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43696         //    viewEl.on('click', this.onClick,  this); 
43697         }
43698          if(this.checked){
43699             this.el.dom.checked =   'checked' ;
43700         }
43701          
43702     } 
43703     
43704     
43705 });//<script type="text/javascript">
43706
43707 /*
43708  * Based  Ext JS Library 1.1.1
43709  * Copyright(c) 2006-2007, Ext JS, LLC.
43710  * LGPL
43711  *
43712  */
43713  
43714 /**
43715  * @class Roo.HtmlEditorCore
43716  * @extends Roo.Component
43717  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43718  *
43719  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43720  */
43721
43722 Roo.HtmlEditorCore = function(config){
43723     
43724     
43725     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43726     
43727     
43728     this.addEvents({
43729         /**
43730          * @event initialize
43731          * Fires when the editor is fully initialized (including the iframe)
43732          * @param {Roo.HtmlEditorCore} this
43733          */
43734         initialize: true,
43735         /**
43736          * @event activate
43737          * Fires when the editor is first receives the focus. Any insertion must wait
43738          * until after this event.
43739          * @param {Roo.HtmlEditorCore} this
43740          */
43741         activate: true,
43742          /**
43743          * @event beforesync
43744          * Fires before the textarea is updated with content from the editor iframe. Return false
43745          * to cancel the sync.
43746          * @param {Roo.HtmlEditorCore} this
43747          * @param {String} html
43748          */
43749         beforesync: true,
43750          /**
43751          * @event beforepush
43752          * Fires before the iframe editor is updated with content from the textarea. Return false
43753          * to cancel the push.
43754          * @param {Roo.HtmlEditorCore} this
43755          * @param {String} html
43756          */
43757         beforepush: true,
43758          /**
43759          * @event sync
43760          * Fires when the textarea is updated with content from the editor iframe.
43761          * @param {Roo.HtmlEditorCore} this
43762          * @param {String} html
43763          */
43764         sync: true,
43765          /**
43766          * @event push
43767          * Fires when the iframe editor is updated with content from the textarea.
43768          * @param {Roo.HtmlEditorCore} this
43769          * @param {String} html
43770          */
43771         push: true,
43772         
43773         /**
43774          * @event editorevent
43775          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43776          * @param {Roo.HtmlEditorCore} this
43777          */
43778         editorevent: true
43779         
43780     });
43781     
43782     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43783     
43784     // defaults : white / black...
43785     this.applyBlacklists();
43786     
43787     
43788     
43789 };
43790
43791
43792 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43793
43794
43795      /**
43796      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43797      */
43798     
43799     owner : false,
43800     
43801      /**
43802      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43803      *                        Roo.resizable.
43804      */
43805     resizable : false,
43806      /**
43807      * @cfg {Number} height (in pixels)
43808      */   
43809     height: 300,
43810    /**
43811      * @cfg {Number} width (in pixels)
43812      */   
43813     width: 500,
43814     
43815     /**
43816      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43817      * 
43818      */
43819     stylesheets: false,
43820     
43821     /**
43822      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
43823      */
43824     allowComments: false,
43825     // id of frame..
43826     frameId: false,
43827     
43828     // private properties
43829     validationEvent : false,
43830     deferHeight: true,
43831     initialized : false,
43832     activated : false,
43833     sourceEditMode : false,
43834     onFocus : Roo.emptyFn,
43835     iframePad:3,
43836     hideMode:'offsets',
43837     
43838     clearUp: true,
43839     
43840     // blacklist + whitelisted elements..
43841     black: false,
43842     white: false,
43843      
43844     bodyCls : '',
43845
43846     /**
43847      * Protected method that will not generally be called directly. It
43848      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43849      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43850      */
43851     getDocMarkup : function(){
43852         // body styles..
43853         var st = '';
43854         
43855         // inherit styels from page...?? 
43856         if (this.stylesheets === false) {
43857             
43858             Roo.get(document.head).select('style').each(function(node) {
43859                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43860             });
43861             
43862             Roo.get(document.head).select('link').each(function(node) { 
43863                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43864             });
43865             
43866         } else if (!this.stylesheets.length) {
43867                 // simple..
43868                 st = '<style type="text/css">' +
43869                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43870                    '</style>';
43871         } else {
43872             for (var i in this.stylesheets) { 
43873                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43874             }
43875             
43876         }
43877         
43878         st +=  '<style type="text/css">' +
43879             'IMG { cursor: pointer } ' +
43880         '</style>';
43881
43882         var cls = 'roo-htmleditor-body';
43883         
43884         if(this.bodyCls.length){
43885             cls += ' ' + this.bodyCls;
43886         }
43887         
43888         return '<html><head>' + st  +
43889             //<style type="text/css">' +
43890             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43891             //'</style>' +
43892             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43893     },
43894
43895     // private
43896     onRender : function(ct, position)
43897     {
43898         var _t = this;
43899         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43900         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43901         
43902         
43903         this.el.dom.style.border = '0 none';
43904         this.el.dom.setAttribute('tabIndex', -1);
43905         this.el.addClass('x-hidden hide');
43906         
43907         
43908         
43909         if(Roo.isIE){ // fix IE 1px bogus margin
43910             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43911         }
43912        
43913         
43914         this.frameId = Roo.id();
43915         
43916          
43917         
43918         var iframe = this.owner.wrap.createChild({
43919             tag: 'iframe',
43920             cls: 'form-control', // bootstrap..
43921             id: this.frameId,
43922             name: this.frameId,
43923             frameBorder : 'no',
43924             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43925         }, this.el
43926         );
43927         
43928         
43929         this.iframe = iframe.dom;
43930
43931          this.assignDocWin();
43932         
43933         this.doc.designMode = 'on';
43934        
43935         this.doc.open();
43936         this.doc.write(this.getDocMarkup());
43937         this.doc.close();
43938
43939         
43940         var task = { // must defer to wait for browser to be ready
43941             run : function(){
43942                 //console.log("run task?" + this.doc.readyState);
43943                 this.assignDocWin();
43944                 if(this.doc.body || this.doc.readyState == 'complete'){
43945                     try {
43946                         this.doc.designMode="on";
43947                     } catch (e) {
43948                         return;
43949                     }
43950                     Roo.TaskMgr.stop(task);
43951                     this.initEditor.defer(10, this);
43952                 }
43953             },
43954             interval : 10,
43955             duration: 10000,
43956             scope: this
43957         };
43958         Roo.TaskMgr.start(task);
43959
43960     },
43961
43962     // private
43963     onResize : function(w, h)
43964     {
43965          Roo.log('resize: ' +w + ',' + h );
43966         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43967         if(!this.iframe){
43968             return;
43969         }
43970         if(typeof w == 'number'){
43971             
43972             this.iframe.style.width = w + 'px';
43973         }
43974         if(typeof h == 'number'){
43975             
43976             this.iframe.style.height = h + 'px';
43977             if(this.doc){
43978                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43979             }
43980         }
43981         
43982     },
43983
43984     /**
43985      * Toggles the editor between standard and source edit mode.
43986      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43987      */
43988     toggleSourceEdit : function(sourceEditMode){
43989         
43990         this.sourceEditMode = sourceEditMode === true;
43991         
43992         if(this.sourceEditMode){
43993  
43994             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43995             
43996         }else{
43997             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43998             //this.iframe.className = '';
43999             this.deferFocus();
44000         }
44001         //this.setSize(this.owner.wrap.getSize());
44002         //this.fireEvent('editmodechange', this, this.sourceEditMode);
44003     },
44004
44005     
44006   
44007
44008     /**
44009      * Protected method that will not generally be called directly. If you need/want
44010      * custom HTML cleanup, this is the method you should override.
44011      * @param {String} html The HTML to be cleaned
44012      * return {String} The cleaned HTML
44013      */
44014     cleanHtml : function(html){
44015         html = String(html);
44016         if(html.length > 5){
44017             if(Roo.isSafari){ // strip safari nonsense
44018                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44019             }
44020         }
44021         if(html == '&nbsp;'){
44022             html = '';
44023         }
44024         return html;
44025     },
44026
44027     /**
44028      * HTML Editor -> Textarea
44029      * Protected method that will not generally be called directly. Syncs the contents
44030      * of the editor iframe with the textarea.
44031      */
44032     syncValue : function(){
44033         if(this.initialized){
44034             var bd = (this.doc.body || this.doc.documentElement);
44035             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44036             var html = bd.innerHTML;
44037             if(Roo.isSafari){
44038                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44039                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44040                 if(m && m[1]){
44041                     html = '<div style="'+m[0]+'">' + html + '</div>';
44042                 }
44043             }
44044             html = this.cleanHtml(html);
44045             // fix up the special chars.. normaly like back quotes in word...
44046             // however we do not want to do this with chinese..
44047             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44048                 
44049                 var cc = match.charCodeAt();
44050
44051                 // Get the character value, handling surrogate pairs
44052                 if (match.length == 2) {
44053                     // It's a surrogate pair, calculate the Unicode code point
44054                     var high = match.charCodeAt(0) - 0xD800;
44055                     var low  = match.charCodeAt(1) - 0xDC00;
44056                     cc = (high * 0x400) + low + 0x10000;
44057                 }  else if (
44058                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44059                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44060                     (cc >= 0xf900 && cc < 0xfb00 )
44061                 ) {
44062                         return match;
44063                 }  
44064          
44065                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44066                 return "&#" + cc + ";";
44067                 
44068                 
44069             });
44070             
44071             
44072              
44073             if(this.owner.fireEvent('beforesync', this, html) !== false){
44074                 this.el.dom.value = html;
44075                 this.owner.fireEvent('sync', this, html);
44076             }
44077         }
44078     },
44079
44080     /**
44081      * Protected method that will not generally be called directly. Pushes the value of the textarea
44082      * into the iframe editor.
44083      */
44084     pushValue : function(){
44085         if(this.initialized){
44086             var v = this.el.dom.value.trim();
44087             
44088 //            if(v.length < 1){
44089 //                v = '&#160;';
44090 //            }
44091             
44092             if(this.owner.fireEvent('beforepush', this, v) !== false){
44093                 var d = (this.doc.body || this.doc.documentElement);
44094                 d.innerHTML = v;
44095                 this.cleanUpPaste();
44096                 this.el.dom.value = d.innerHTML;
44097                 this.owner.fireEvent('push', this, v);
44098             }
44099         }
44100     },
44101
44102     // private
44103     deferFocus : function(){
44104         this.focus.defer(10, this);
44105     },
44106
44107     // doc'ed in Field
44108     focus : function(){
44109         if(this.win && !this.sourceEditMode){
44110             this.win.focus();
44111         }else{
44112             this.el.focus();
44113         }
44114     },
44115     
44116     assignDocWin: function()
44117     {
44118         var iframe = this.iframe;
44119         
44120          if(Roo.isIE){
44121             this.doc = iframe.contentWindow.document;
44122             this.win = iframe.contentWindow;
44123         } else {
44124 //            if (!Roo.get(this.frameId)) {
44125 //                return;
44126 //            }
44127 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44128 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44129             
44130             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44131                 return;
44132             }
44133             
44134             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44135             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44136         }
44137     },
44138     
44139     // private
44140     initEditor : function(){
44141         //console.log("INIT EDITOR");
44142         this.assignDocWin();
44143         
44144         
44145         
44146         this.doc.designMode="on";
44147         this.doc.open();
44148         this.doc.write(this.getDocMarkup());
44149         this.doc.close();
44150         
44151         var dbody = (this.doc.body || this.doc.documentElement);
44152         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44153         // this copies styles from the containing element into thsi one..
44154         // not sure why we need all of this..
44155         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44156         
44157         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44158         //ss['background-attachment'] = 'fixed'; // w3c
44159         dbody.bgProperties = 'fixed'; // ie
44160         //Roo.DomHelper.applyStyles(dbody, ss);
44161         Roo.EventManager.on(this.doc, {
44162             //'mousedown': this.onEditorEvent,
44163             'mouseup': this.onEditorEvent,
44164             'dblclick': this.onEditorEvent,
44165             'click': this.onEditorEvent,
44166             'keyup': this.onEditorEvent,
44167             buffer:100,
44168             scope: this
44169         });
44170         if(Roo.isGecko){
44171             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44172         }
44173         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44174             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44175         }
44176         this.initialized = true;
44177
44178         this.owner.fireEvent('initialize', this);
44179         this.pushValue();
44180     },
44181
44182     // private
44183     onDestroy : function(){
44184         
44185         
44186         
44187         if(this.rendered){
44188             
44189             //for (var i =0; i < this.toolbars.length;i++) {
44190             //    // fixme - ask toolbars for heights?
44191             //    this.toolbars[i].onDestroy();
44192            // }
44193             
44194             //this.wrap.dom.innerHTML = '';
44195             //this.wrap.remove();
44196         }
44197     },
44198
44199     // private
44200     onFirstFocus : function(){
44201         
44202         this.assignDocWin();
44203         
44204         
44205         this.activated = true;
44206          
44207     
44208         if(Roo.isGecko){ // prevent silly gecko errors
44209             this.win.focus();
44210             var s = this.win.getSelection();
44211             if(!s.focusNode || s.focusNode.nodeType != 3){
44212                 var r = s.getRangeAt(0);
44213                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44214                 r.collapse(true);
44215                 this.deferFocus();
44216             }
44217             try{
44218                 this.execCmd('useCSS', true);
44219                 this.execCmd('styleWithCSS', false);
44220             }catch(e){}
44221         }
44222         this.owner.fireEvent('activate', this);
44223     },
44224
44225     // private
44226     adjustFont: function(btn){
44227         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44228         //if(Roo.isSafari){ // safari
44229         //    adjust *= 2;
44230        // }
44231         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44232         if(Roo.isSafari){ // safari
44233             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44234             v =  (v < 10) ? 10 : v;
44235             v =  (v > 48) ? 48 : v;
44236             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44237             
44238         }
44239         
44240         
44241         v = Math.max(1, v+adjust);
44242         
44243         this.execCmd('FontSize', v  );
44244     },
44245
44246     onEditorEvent : function(e)
44247     {
44248         this.owner.fireEvent('editorevent', this, e);
44249       //  this.updateToolbar();
44250         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44251     },
44252
44253     insertTag : function(tg)
44254     {
44255         // could be a bit smarter... -> wrap the current selected tRoo..
44256         if (tg.toLowerCase() == 'span' ||
44257             tg.toLowerCase() == 'code' ||
44258             tg.toLowerCase() == 'sup' ||
44259             tg.toLowerCase() == 'sub' 
44260             ) {
44261             
44262             range = this.createRange(this.getSelection());
44263             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44264             wrappingNode.appendChild(range.extractContents());
44265             range.insertNode(wrappingNode);
44266
44267             return;
44268             
44269             
44270             
44271         }
44272         this.execCmd("formatblock",   tg);
44273         
44274     },
44275     
44276     insertText : function(txt)
44277     {
44278         
44279         
44280         var range = this.createRange();
44281         range.deleteContents();
44282                //alert(Sender.getAttribute('label'));
44283                
44284         range.insertNode(this.doc.createTextNode(txt));
44285     } ,
44286     
44287      
44288
44289     /**
44290      * Executes a Midas editor command on the editor document and performs necessary focus and
44291      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44292      * @param {String} cmd The Midas command
44293      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44294      */
44295     relayCmd : function(cmd, value){
44296         this.win.focus();
44297         this.execCmd(cmd, value);
44298         this.owner.fireEvent('editorevent', this);
44299         //this.updateToolbar();
44300         this.owner.deferFocus();
44301     },
44302
44303     /**
44304      * Executes a Midas editor command directly on the editor document.
44305      * For visual commands, you should use {@link #relayCmd} instead.
44306      * <b>This should only be called after the editor is initialized.</b>
44307      * @param {String} cmd The Midas command
44308      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44309      */
44310     execCmd : function(cmd, value){
44311         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44312         this.syncValue();
44313     },
44314  
44315  
44316    
44317     /**
44318      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44319      * to insert tRoo.
44320      * @param {String} text | dom node.. 
44321      */
44322     insertAtCursor : function(text)
44323     {
44324         
44325         if(!this.activated){
44326             return;
44327         }
44328         /*
44329         if(Roo.isIE){
44330             this.win.focus();
44331             var r = this.doc.selection.createRange();
44332             if(r){
44333                 r.collapse(true);
44334                 r.pasteHTML(text);
44335                 this.syncValue();
44336                 this.deferFocus();
44337             
44338             }
44339             return;
44340         }
44341         */
44342         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44343             this.win.focus();
44344             
44345             
44346             // from jquery ui (MIT licenced)
44347             var range, node;
44348             var win = this.win;
44349             
44350             if (win.getSelection && win.getSelection().getRangeAt) {
44351                 range = win.getSelection().getRangeAt(0);
44352                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44353                 range.insertNode(node);
44354             } else if (win.document.selection && win.document.selection.createRange) {
44355                 // no firefox support
44356                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44357                 win.document.selection.createRange().pasteHTML(txt);
44358             } else {
44359                 // no firefox support
44360                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44361                 this.execCmd('InsertHTML', txt);
44362             } 
44363             
44364             this.syncValue();
44365             
44366             this.deferFocus();
44367         }
44368     },
44369  // private
44370     mozKeyPress : function(e){
44371         if(e.ctrlKey){
44372             var c = e.getCharCode(), cmd;
44373           
44374             if(c > 0){
44375                 c = String.fromCharCode(c).toLowerCase();
44376                 switch(c){
44377                     case 'b':
44378                         cmd = 'bold';
44379                         break;
44380                     case 'i':
44381                         cmd = 'italic';
44382                         break;
44383                     
44384                     case 'u':
44385                         cmd = 'underline';
44386                         break;
44387                     
44388                     case 'v':
44389                         this.cleanUpPaste.defer(100, this);
44390                         return;
44391                         
44392                 }
44393                 if(cmd){
44394                     this.win.focus();
44395                     this.execCmd(cmd);
44396                     this.deferFocus();
44397                     e.preventDefault();
44398                 }
44399                 
44400             }
44401         }
44402     },
44403
44404     // private
44405     fixKeys : function(){ // load time branching for fastest keydown performance
44406         if(Roo.isIE){
44407             return function(e){
44408                 var k = e.getKey(), r;
44409                 if(k == e.TAB){
44410                     e.stopEvent();
44411                     r = this.doc.selection.createRange();
44412                     if(r){
44413                         r.collapse(true);
44414                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44415                         this.deferFocus();
44416                     }
44417                     return;
44418                 }
44419                 
44420                 if(k == e.ENTER){
44421                     r = this.doc.selection.createRange();
44422                     if(r){
44423                         var target = r.parentElement();
44424                         if(!target || target.tagName.toLowerCase() != 'li'){
44425                             e.stopEvent();
44426                             r.pasteHTML('<br />');
44427                             r.collapse(false);
44428                             r.select();
44429                         }
44430                     }
44431                 }
44432                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44433                     this.cleanUpPaste.defer(100, this);
44434                     return;
44435                 }
44436                 
44437                 
44438             };
44439         }else if(Roo.isOpera){
44440             return function(e){
44441                 var k = e.getKey();
44442                 if(k == e.TAB){
44443                     e.stopEvent();
44444                     this.win.focus();
44445                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44446                     this.deferFocus();
44447                 }
44448                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44449                     this.cleanUpPaste.defer(100, this);
44450                     return;
44451                 }
44452                 
44453             };
44454         }else if(Roo.isSafari){
44455             return function(e){
44456                 var k = e.getKey();
44457                 
44458                 if(k == e.TAB){
44459                     e.stopEvent();
44460                     this.execCmd('InsertText','\t');
44461                     this.deferFocus();
44462                     return;
44463                 }
44464                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44465                     this.cleanUpPaste.defer(100, this);
44466                     return;
44467                 }
44468                 
44469              };
44470         }
44471     }(),
44472     
44473     getAllAncestors: function()
44474     {
44475         var p = this.getSelectedNode();
44476         var a = [];
44477         if (!p) {
44478             a.push(p); // push blank onto stack..
44479             p = this.getParentElement();
44480         }
44481         
44482         
44483         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44484             a.push(p);
44485             p = p.parentNode;
44486         }
44487         a.push(this.doc.body);
44488         return a;
44489     },
44490     lastSel : false,
44491     lastSelNode : false,
44492     
44493     
44494     getSelection : function() 
44495     {
44496         this.assignDocWin();
44497         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44498     },
44499     
44500     getSelectedNode: function() 
44501     {
44502         // this may only work on Gecko!!!
44503         
44504         // should we cache this!!!!
44505         
44506         
44507         
44508          
44509         var range = this.createRange(this.getSelection()).cloneRange();
44510         
44511         if (Roo.isIE) {
44512             var parent = range.parentElement();
44513             while (true) {
44514                 var testRange = range.duplicate();
44515                 testRange.moveToElementText(parent);
44516                 if (testRange.inRange(range)) {
44517                     break;
44518                 }
44519                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44520                     break;
44521                 }
44522                 parent = parent.parentElement;
44523             }
44524             return parent;
44525         }
44526         
44527         // is ancestor a text element.
44528         var ac =  range.commonAncestorContainer;
44529         if (ac.nodeType == 3) {
44530             ac = ac.parentNode;
44531         }
44532         
44533         var ar = ac.childNodes;
44534          
44535         var nodes = [];
44536         var other_nodes = [];
44537         var has_other_nodes = false;
44538         for (var i=0;i<ar.length;i++) {
44539             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44540                 continue;
44541             }
44542             // fullly contained node.
44543             
44544             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44545                 nodes.push(ar[i]);
44546                 continue;
44547             }
44548             
44549             // probably selected..
44550             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44551                 other_nodes.push(ar[i]);
44552                 continue;
44553             }
44554             // outer..
44555             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44556                 continue;
44557             }
44558             
44559             
44560             has_other_nodes = true;
44561         }
44562         if (!nodes.length && other_nodes.length) {
44563             nodes= other_nodes;
44564         }
44565         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44566             return false;
44567         }
44568         
44569         return nodes[0];
44570     },
44571     createRange: function(sel)
44572     {
44573         // this has strange effects when using with 
44574         // top toolbar - not sure if it's a great idea.
44575         //this.editor.contentWindow.focus();
44576         if (typeof sel != "undefined") {
44577             try {
44578                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44579             } catch(e) {
44580                 return this.doc.createRange();
44581             }
44582         } else {
44583             return this.doc.createRange();
44584         }
44585     },
44586     getParentElement: function()
44587     {
44588         
44589         this.assignDocWin();
44590         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44591         
44592         var range = this.createRange(sel);
44593          
44594         try {
44595             var p = range.commonAncestorContainer;
44596             while (p.nodeType == 3) { // text node
44597                 p = p.parentNode;
44598             }
44599             return p;
44600         } catch (e) {
44601             return null;
44602         }
44603     
44604     },
44605     /***
44606      *
44607      * Range intersection.. the hard stuff...
44608      *  '-1' = before
44609      *  '0' = hits..
44610      *  '1' = after.
44611      *         [ -- selected range --- ]
44612      *   [fail]                        [fail]
44613      *
44614      *    basically..
44615      *      if end is before start or  hits it. fail.
44616      *      if start is after end or hits it fail.
44617      *
44618      *   if either hits (but other is outside. - then it's not 
44619      *   
44620      *    
44621      **/
44622     
44623     
44624     // @see http://www.thismuchiknow.co.uk/?p=64.
44625     rangeIntersectsNode : function(range, node)
44626     {
44627         var nodeRange = node.ownerDocument.createRange();
44628         try {
44629             nodeRange.selectNode(node);
44630         } catch (e) {
44631             nodeRange.selectNodeContents(node);
44632         }
44633     
44634         var rangeStartRange = range.cloneRange();
44635         rangeStartRange.collapse(true);
44636     
44637         var rangeEndRange = range.cloneRange();
44638         rangeEndRange.collapse(false);
44639     
44640         var nodeStartRange = nodeRange.cloneRange();
44641         nodeStartRange.collapse(true);
44642     
44643         var nodeEndRange = nodeRange.cloneRange();
44644         nodeEndRange.collapse(false);
44645     
44646         return rangeStartRange.compareBoundaryPoints(
44647                  Range.START_TO_START, nodeEndRange) == -1 &&
44648                rangeEndRange.compareBoundaryPoints(
44649                  Range.START_TO_START, nodeStartRange) == 1;
44650         
44651          
44652     },
44653     rangeCompareNode : function(range, node)
44654     {
44655         var nodeRange = node.ownerDocument.createRange();
44656         try {
44657             nodeRange.selectNode(node);
44658         } catch (e) {
44659             nodeRange.selectNodeContents(node);
44660         }
44661         
44662         
44663         range.collapse(true);
44664     
44665         nodeRange.collapse(true);
44666      
44667         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44668         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44669          
44670         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44671         
44672         var nodeIsBefore   =  ss == 1;
44673         var nodeIsAfter    = ee == -1;
44674         
44675         if (nodeIsBefore && nodeIsAfter) {
44676             return 0; // outer
44677         }
44678         if (!nodeIsBefore && nodeIsAfter) {
44679             return 1; //right trailed.
44680         }
44681         
44682         if (nodeIsBefore && !nodeIsAfter) {
44683             return 2;  // left trailed.
44684         }
44685         // fully contined.
44686         return 3;
44687     },
44688
44689     // private? - in a new class?
44690     cleanUpPaste :  function()
44691     {
44692         // cleans up the whole document..
44693         Roo.log('cleanuppaste');
44694         
44695         this.cleanUpChildren(this.doc.body);
44696         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44697         if (clean != this.doc.body.innerHTML) {
44698             this.doc.body.innerHTML = clean;
44699         }
44700         
44701     },
44702     
44703     cleanWordChars : function(input) {// change the chars to hex code
44704         var he = Roo.HtmlEditorCore;
44705         
44706         var output = input;
44707         Roo.each(he.swapCodes, function(sw) { 
44708             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44709             
44710             output = output.replace(swapper, sw[1]);
44711         });
44712         
44713         return output;
44714     },
44715     
44716     
44717     cleanUpChildren : function (n)
44718     {
44719         if (!n.childNodes.length) {
44720             return;
44721         }
44722         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44723            this.cleanUpChild(n.childNodes[i]);
44724         }
44725     },
44726     
44727     
44728         
44729     
44730     cleanUpChild : function (node)
44731     {
44732         var ed = this;
44733         //console.log(node);
44734         if (node.nodeName == "#text") {
44735             // clean up silly Windows -- stuff?
44736             return; 
44737         }
44738         if (node.nodeName == "#comment") {
44739             if (!this.allowComments) {
44740                 node.parentNode.removeChild(node);
44741             }
44742             // clean up silly Windows -- stuff?
44743             return; 
44744         }
44745         var lcname = node.tagName.toLowerCase();
44746         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44747         // whitelist of tags..
44748         
44749         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44750             // remove node.
44751             node.parentNode.removeChild(node);
44752             return;
44753             
44754         }
44755         
44756         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44757         
44758         // spans with no attributes - just remove them..
44759         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44760             remove_keep_children = true;
44761         }
44762         
44763         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44764         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44765         
44766         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44767         //    remove_keep_children = true;
44768         //}
44769         
44770         if (remove_keep_children) {
44771             this.cleanUpChildren(node);
44772             // inserts everything just before this node...
44773             while (node.childNodes.length) {
44774                 var cn = node.childNodes[0];
44775                 node.removeChild(cn);
44776                 node.parentNode.insertBefore(cn, node);
44777             }
44778             node.parentNode.removeChild(node);
44779             return;
44780         }
44781         
44782         if (!node.attributes || !node.attributes.length) {
44783             
44784           
44785             
44786             
44787             this.cleanUpChildren(node);
44788             return;
44789         }
44790         
44791         function cleanAttr(n,v)
44792         {
44793             
44794             if (v.match(/^\./) || v.match(/^\//)) {
44795                 return;
44796             }
44797             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44798                 return;
44799             }
44800             if (v.match(/^#/)) {
44801                 return;
44802             }
44803             if (v.match(/^\{/)) { // allow template editing.
44804                 return;
44805             }
44806 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44807             node.removeAttribute(n);
44808             
44809         }
44810         
44811         var cwhite = this.cwhite;
44812         var cblack = this.cblack;
44813             
44814         function cleanStyle(n,v)
44815         {
44816             if (v.match(/expression/)) { //XSS?? should we even bother..
44817                 node.removeAttribute(n);
44818                 return;
44819             }
44820             
44821             var parts = v.split(/;/);
44822             var clean = [];
44823             
44824             Roo.each(parts, function(p) {
44825                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44826                 if (!p.length) {
44827                     return true;
44828                 }
44829                 var l = p.split(':').shift().replace(/\s+/g,'');
44830                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44831                 
44832                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44833 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44834                     //node.removeAttribute(n);
44835                     return true;
44836                 }
44837                 //Roo.log()
44838                 // only allow 'c whitelisted system attributes'
44839                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44840 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44841                     //node.removeAttribute(n);
44842                     return true;
44843                 }
44844                 
44845                 
44846                  
44847                 
44848                 clean.push(p);
44849                 return true;
44850             });
44851             if (clean.length) { 
44852                 node.setAttribute(n, clean.join(';'));
44853             } else {
44854                 node.removeAttribute(n);
44855             }
44856             
44857         }
44858         
44859         
44860         for (var i = node.attributes.length-1; i > -1 ; i--) {
44861             var a = node.attributes[i];
44862             //console.log(a);
44863             
44864             if (a.name.toLowerCase().substr(0,2)=='on')  {
44865                 node.removeAttribute(a.name);
44866                 continue;
44867             }
44868             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44869                 node.removeAttribute(a.name);
44870                 continue;
44871             }
44872             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44873                 cleanAttr(a.name,a.value); // fixme..
44874                 continue;
44875             }
44876             if (a.name == 'style') {
44877                 cleanStyle(a.name,a.value);
44878                 continue;
44879             }
44880             /// clean up MS crap..
44881             // tecnically this should be a list of valid class'es..
44882             
44883             
44884             if (a.name == 'class') {
44885                 if (a.value.match(/^Mso/)) {
44886                     node.removeAttribute('class');
44887                 }
44888                 
44889                 if (a.value.match(/^body$/)) {
44890                     node.removeAttribute('class');
44891                 }
44892                 continue;
44893             }
44894             
44895             // style cleanup!?
44896             // class cleanup?
44897             
44898         }
44899         
44900         
44901         this.cleanUpChildren(node);
44902         
44903         
44904     },
44905     
44906     /**
44907      * Clean up MS wordisms...
44908      */
44909     cleanWord : function(node)
44910     {
44911         if (!node) {
44912             this.cleanWord(this.doc.body);
44913             return;
44914         }
44915         
44916         if(
44917                 node.nodeName == 'SPAN' &&
44918                 !node.hasAttributes() &&
44919                 node.childNodes.length == 1 &&
44920                 node.firstChild.nodeName == "#text"  
44921         ) {
44922             var textNode = node.firstChild;
44923             node.removeChild(textNode);
44924             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44925                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44926             }
44927             node.parentNode.insertBefore(textNode, node);
44928             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44929                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44930             }
44931             node.parentNode.removeChild(node);
44932         }
44933         
44934         if (node.nodeName == "#text") {
44935             // clean up silly Windows -- stuff?
44936             return; 
44937         }
44938         if (node.nodeName == "#comment") {
44939             node.parentNode.removeChild(node);
44940             // clean up silly Windows -- stuff?
44941             return; 
44942         }
44943         
44944         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44945             node.parentNode.removeChild(node);
44946             return;
44947         }
44948         //Roo.log(node.tagName);
44949         // remove - but keep children..
44950         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44951             //Roo.log('-- removed');
44952             while (node.childNodes.length) {
44953                 var cn = node.childNodes[0];
44954                 node.removeChild(cn);
44955                 node.parentNode.insertBefore(cn, node);
44956                 // move node to parent - and clean it..
44957                 this.cleanWord(cn);
44958             }
44959             node.parentNode.removeChild(node);
44960             /// no need to iterate chidlren = it's got none..
44961             //this.iterateChildren(node, this.cleanWord);
44962             return;
44963         }
44964         // clean styles
44965         if (node.className.length) {
44966             
44967             var cn = node.className.split(/\W+/);
44968             var cna = [];
44969             Roo.each(cn, function(cls) {
44970                 if (cls.match(/Mso[a-zA-Z]+/)) {
44971                     return;
44972                 }
44973                 cna.push(cls);
44974             });
44975             node.className = cna.length ? cna.join(' ') : '';
44976             if (!cna.length) {
44977                 node.removeAttribute("class");
44978             }
44979         }
44980         
44981         if (node.hasAttribute("lang")) {
44982             node.removeAttribute("lang");
44983         }
44984         
44985         if (node.hasAttribute("style")) {
44986             
44987             var styles = node.getAttribute("style").split(";");
44988             var nstyle = [];
44989             Roo.each(styles, function(s) {
44990                 if (!s.match(/:/)) {
44991                     return;
44992                 }
44993                 var kv = s.split(":");
44994                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44995                     return;
44996                 }
44997                 // what ever is left... we allow.
44998                 nstyle.push(s);
44999             });
45000             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45001             if (!nstyle.length) {
45002                 node.removeAttribute('style');
45003             }
45004         }
45005         this.iterateChildren(node, this.cleanWord);
45006         
45007         
45008         
45009     },
45010     /**
45011      * iterateChildren of a Node, calling fn each time, using this as the scole..
45012      * @param {DomNode} node node to iterate children of.
45013      * @param {Function} fn method of this class to call on each item.
45014      */
45015     iterateChildren : function(node, fn)
45016     {
45017         if (!node.childNodes.length) {
45018                 return;
45019         }
45020         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45021            fn.call(this, node.childNodes[i])
45022         }
45023     },
45024     
45025     
45026     /**
45027      * cleanTableWidths.
45028      *
45029      * Quite often pasting from word etc.. results in tables with column and widths.
45030      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45031      *
45032      */
45033     cleanTableWidths : function(node)
45034     {
45035          
45036          
45037         if (!node) {
45038             this.cleanTableWidths(this.doc.body);
45039             return;
45040         }
45041         
45042         // ignore list...
45043         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45044             return; 
45045         }
45046         Roo.log(node.tagName);
45047         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45048             this.iterateChildren(node, this.cleanTableWidths);
45049             return;
45050         }
45051         if (node.hasAttribute('width')) {
45052             node.removeAttribute('width');
45053         }
45054         
45055          
45056         if (node.hasAttribute("style")) {
45057             // pretty basic...
45058             
45059             var styles = node.getAttribute("style").split(";");
45060             var nstyle = [];
45061             Roo.each(styles, function(s) {
45062                 if (!s.match(/:/)) {
45063                     return;
45064                 }
45065                 var kv = s.split(":");
45066                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45067                     return;
45068                 }
45069                 // what ever is left... we allow.
45070                 nstyle.push(s);
45071             });
45072             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45073             if (!nstyle.length) {
45074                 node.removeAttribute('style');
45075             }
45076         }
45077         
45078         this.iterateChildren(node, this.cleanTableWidths);
45079         
45080         
45081     },
45082     
45083     
45084     
45085     
45086     domToHTML : function(currentElement, depth, nopadtext) {
45087         
45088         depth = depth || 0;
45089         nopadtext = nopadtext || false;
45090     
45091         if (!currentElement) {
45092             return this.domToHTML(this.doc.body);
45093         }
45094         
45095         //Roo.log(currentElement);
45096         var j;
45097         var allText = false;
45098         var nodeName = currentElement.nodeName;
45099         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45100         
45101         if  (nodeName == '#text') {
45102             
45103             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45104         }
45105         
45106         
45107         var ret = '';
45108         if (nodeName != 'BODY') {
45109              
45110             var i = 0;
45111             // Prints the node tagName, such as <A>, <IMG>, etc
45112             if (tagName) {
45113                 var attr = [];
45114                 for(i = 0; i < currentElement.attributes.length;i++) {
45115                     // quoting?
45116                     var aname = currentElement.attributes.item(i).name;
45117                     if (!currentElement.attributes.item(i).value.length) {
45118                         continue;
45119                     }
45120                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45121                 }
45122                 
45123                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45124             } 
45125             else {
45126                 
45127                 // eack
45128             }
45129         } else {
45130             tagName = false;
45131         }
45132         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45133             return ret;
45134         }
45135         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45136             nopadtext = true;
45137         }
45138         
45139         
45140         // Traverse the tree
45141         i = 0;
45142         var currentElementChild = currentElement.childNodes.item(i);
45143         var allText = true;
45144         var innerHTML  = '';
45145         lastnode = '';
45146         while (currentElementChild) {
45147             // Formatting code (indent the tree so it looks nice on the screen)
45148             var nopad = nopadtext;
45149             if (lastnode == 'SPAN') {
45150                 nopad  = true;
45151             }
45152             // text
45153             if  (currentElementChild.nodeName == '#text') {
45154                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45155                 toadd = nopadtext ? toadd : toadd.trim();
45156                 if (!nopad && toadd.length > 80) {
45157                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45158                 }
45159                 innerHTML  += toadd;
45160                 
45161                 i++;
45162                 currentElementChild = currentElement.childNodes.item(i);
45163                 lastNode = '';
45164                 continue;
45165             }
45166             allText = false;
45167             
45168             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45169                 
45170             // Recursively traverse the tree structure of the child node
45171             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45172             lastnode = currentElementChild.nodeName;
45173             i++;
45174             currentElementChild=currentElement.childNodes.item(i);
45175         }
45176         
45177         ret += innerHTML;
45178         
45179         if (!allText) {
45180                 // The remaining code is mostly for formatting the tree
45181             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45182         }
45183         
45184         
45185         if (tagName) {
45186             ret+= "</"+tagName+">";
45187         }
45188         return ret;
45189         
45190     },
45191         
45192     applyBlacklists : function()
45193     {
45194         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45195         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45196         
45197         this.white = [];
45198         this.black = [];
45199         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45200             if (b.indexOf(tag) > -1) {
45201                 return;
45202             }
45203             this.white.push(tag);
45204             
45205         }, this);
45206         
45207         Roo.each(w, function(tag) {
45208             if (b.indexOf(tag) > -1) {
45209                 return;
45210             }
45211             if (this.white.indexOf(tag) > -1) {
45212                 return;
45213             }
45214             this.white.push(tag);
45215             
45216         }, this);
45217         
45218         
45219         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45220             if (w.indexOf(tag) > -1) {
45221                 return;
45222             }
45223             this.black.push(tag);
45224             
45225         }, this);
45226         
45227         Roo.each(b, function(tag) {
45228             if (w.indexOf(tag) > -1) {
45229                 return;
45230             }
45231             if (this.black.indexOf(tag) > -1) {
45232                 return;
45233             }
45234             this.black.push(tag);
45235             
45236         }, this);
45237         
45238         
45239         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45240         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45241         
45242         this.cwhite = [];
45243         this.cblack = [];
45244         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45245             if (b.indexOf(tag) > -1) {
45246                 return;
45247             }
45248             this.cwhite.push(tag);
45249             
45250         }, this);
45251         
45252         Roo.each(w, function(tag) {
45253             if (b.indexOf(tag) > -1) {
45254                 return;
45255             }
45256             if (this.cwhite.indexOf(tag) > -1) {
45257                 return;
45258             }
45259             this.cwhite.push(tag);
45260             
45261         }, this);
45262         
45263         
45264         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45265             if (w.indexOf(tag) > -1) {
45266                 return;
45267             }
45268             this.cblack.push(tag);
45269             
45270         }, this);
45271         
45272         Roo.each(b, function(tag) {
45273             if (w.indexOf(tag) > -1) {
45274                 return;
45275             }
45276             if (this.cblack.indexOf(tag) > -1) {
45277                 return;
45278             }
45279             this.cblack.push(tag);
45280             
45281         }, this);
45282     },
45283     
45284     setStylesheets : function(stylesheets)
45285     {
45286         if(typeof(stylesheets) == 'string'){
45287             Roo.get(this.iframe.contentDocument.head).createChild({
45288                 tag : 'link',
45289                 rel : 'stylesheet',
45290                 type : 'text/css',
45291                 href : stylesheets
45292             });
45293             
45294             return;
45295         }
45296         var _this = this;
45297      
45298         Roo.each(stylesheets, function(s) {
45299             if(!s.length){
45300                 return;
45301             }
45302             
45303             Roo.get(_this.iframe.contentDocument.head).createChild({
45304                 tag : 'link',
45305                 rel : 'stylesheet',
45306                 type : 'text/css',
45307                 href : s
45308             });
45309         });
45310
45311         
45312     },
45313     
45314     removeStylesheets : function()
45315     {
45316         var _this = this;
45317         
45318         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45319             s.remove();
45320         });
45321     },
45322     
45323     setStyle : function(style)
45324     {
45325         Roo.get(this.iframe.contentDocument.head).createChild({
45326             tag : 'style',
45327             type : 'text/css',
45328             html : style
45329         });
45330
45331         return;
45332     }
45333     
45334     // hide stuff that is not compatible
45335     /**
45336      * @event blur
45337      * @hide
45338      */
45339     /**
45340      * @event change
45341      * @hide
45342      */
45343     /**
45344      * @event focus
45345      * @hide
45346      */
45347     /**
45348      * @event specialkey
45349      * @hide
45350      */
45351     /**
45352      * @cfg {String} fieldClass @hide
45353      */
45354     /**
45355      * @cfg {String} focusClass @hide
45356      */
45357     /**
45358      * @cfg {String} autoCreate @hide
45359      */
45360     /**
45361      * @cfg {String} inputType @hide
45362      */
45363     /**
45364      * @cfg {String} invalidClass @hide
45365      */
45366     /**
45367      * @cfg {String} invalidText @hide
45368      */
45369     /**
45370      * @cfg {String} msgFx @hide
45371      */
45372     /**
45373      * @cfg {String} validateOnBlur @hide
45374      */
45375 });
45376
45377 Roo.HtmlEditorCore.white = [
45378         'area', 'br', 'img', 'input', 'hr', 'wbr',
45379         
45380        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45381        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45382        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45383        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45384        'table',   'ul',         'xmp', 
45385        
45386        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45387       'thead',   'tr', 
45388      
45389       'dir', 'menu', 'ol', 'ul', 'dl',
45390        
45391       'embed',  'object'
45392 ];
45393
45394
45395 Roo.HtmlEditorCore.black = [
45396     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45397         'applet', // 
45398         'base',   'basefont', 'bgsound', 'blink',  'body', 
45399         'frame',  'frameset', 'head',    'html',   'ilayer', 
45400         'iframe', 'layer',  'link',     'meta',    'object',   
45401         'script', 'style' ,'title',  'xml' // clean later..
45402 ];
45403 Roo.HtmlEditorCore.clean = [
45404     'script', 'style', 'title', 'xml'
45405 ];
45406 Roo.HtmlEditorCore.remove = [
45407     'font'
45408 ];
45409 // attributes..
45410
45411 Roo.HtmlEditorCore.ablack = [
45412     'on'
45413 ];
45414     
45415 Roo.HtmlEditorCore.aclean = [ 
45416     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45417 ];
45418
45419 // protocols..
45420 Roo.HtmlEditorCore.pwhite= [
45421         'http',  'https',  'mailto'
45422 ];
45423
45424 // white listed style attributes.
45425 Roo.HtmlEditorCore.cwhite= [
45426       //  'text-align', /// default is to allow most things..
45427       
45428          
45429 //        'font-size'//??
45430 ];
45431
45432 // black listed style attributes.
45433 Roo.HtmlEditorCore.cblack= [
45434       //  'font-size' -- this can be set by the project 
45435 ];
45436
45437
45438 Roo.HtmlEditorCore.swapCodes   =[ 
45439     [    8211, "&#8211;" ], 
45440     [    8212, "&#8212;" ], 
45441     [    8216,  "'" ],  
45442     [    8217, "'" ],  
45443     [    8220, '"' ],  
45444     [    8221, '"' ],  
45445     [    8226, "*" ],  
45446     [    8230, "..." ]
45447 ]; 
45448
45449     //<script type="text/javascript">
45450
45451 /*
45452  * Ext JS Library 1.1.1
45453  * Copyright(c) 2006-2007, Ext JS, LLC.
45454  * Licence LGPL
45455  * 
45456  */
45457  
45458  
45459 Roo.form.HtmlEditor = function(config){
45460     
45461     
45462     
45463     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45464     
45465     if (!this.toolbars) {
45466         this.toolbars = [];
45467     }
45468     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45469     
45470     
45471 };
45472
45473 /**
45474  * @class Roo.form.HtmlEditor
45475  * @extends Roo.form.Field
45476  * Provides a lightweight HTML Editor component.
45477  *
45478  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45479  * 
45480  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45481  * supported by this editor.</b><br/><br/>
45482  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45483  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45484  */
45485 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45486     /**
45487      * @cfg {Boolean} clearUp
45488      */
45489     clearUp : true,
45490       /**
45491      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45492      */
45493     toolbars : false,
45494    
45495      /**
45496      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45497      *                        Roo.resizable.
45498      */
45499     resizable : false,
45500      /**
45501      * @cfg {Number} height (in pixels)
45502      */   
45503     height: 300,
45504    /**
45505      * @cfg {Number} width (in pixels)
45506      */   
45507     width: 500,
45508     
45509     /**
45510      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45511      * 
45512      */
45513     stylesheets: false,
45514     
45515     
45516      /**
45517      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45518      * 
45519      */
45520     cblack: false,
45521     /**
45522      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45523      * 
45524      */
45525     cwhite: false,
45526     
45527      /**
45528      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45529      * 
45530      */
45531     black: false,
45532     /**
45533      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45534      * 
45535      */
45536     white: false,
45537     /**
45538      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45539      */
45540     allowComments: false,
45541     
45542     // id of frame..
45543     frameId: false,
45544     
45545     // private properties
45546     validationEvent : false,
45547     deferHeight: true,
45548     initialized : false,
45549     activated : false,
45550     
45551     onFocus : Roo.emptyFn,
45552     iframePad:3,
45553     hideMode:'offsets',
45554     
45555     actionMode : 'container', // defaults to hiding it...
45556     
45557     defaultAutoCreate : { // modified by initCompnoent..
45558         tag: "textarea",
45559         style:"width:500px;height:300px;",
45560         autocomplete: "new-password"
45561     },
45562
45563     // private
45564     initComponent : function(){
45565         this.addEvents({
45566             /**
45567              * @event initialize
45568              * Fires when the editor is fully initialized (including the iframe)
45569              * @param {HtmlEditor} this
45570              */
45571             initialize: true,
45572             /**
45573              * @event activate
45574              * Fires when the editor is first receives the focus. Any insertion must wait
45575              * until after this event.
45576              * @param {HtmlEditor} this
45577              */
45578             activate: true,
45579              /**
45580              * @event beforesync
45581              * Fires before the textarea is updated with content from the editor iframe. Return false
45582              * to cancel the sync.
45583              * @param {HtmlEditor} this
45584              * @param {String} html
45585              */
45586             beforesync: true,
45587              /**
45588              * @event beforepush
45589              * Fires before the iframe editor is updated with content from the textarea. Return false
45590              * to cancel the push.
45591              * @param {HtmlEditor} this
45592              * @param {String} html
45593              */
45594             beforepush: true,
45595              /**
45596              * @event sync
45597              * Fires when the textarea is updated with content from the editor iframe.
45598              * @param {HtmlEditor} this
45599              * @param {String} html
45600              */
45601             sync: true,
45602              /**
45603              * @event push
45604              * Fires when the iframe editor is updated with content from the textarea.
45605              * @param {HtmlEditor} this
45606              * @param {String} html
45607              */
45608             push: true,
45609              /**
45610              * @event editmodechange
45611              * Fires when the editor switches edit modes
45612              * @param {HtmlEditor} this
45613              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45614              */
45615             editmodechange: true,
45616             /**
45617              * @event editorevent
45618              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45619              * @param {HtmlEditor} this
45620              */
45621             editorevent: true,
45622             /**
45623              * @event firstfocus
45624              * Fires when on first focus - needed by toolbars..
45625              * @param {HtmlEditor} this
45626              */
45627             firstfocus: true,
45628             /**
45629              * @event autosave
45630              * Auto save the htmlEditor value as a file into Events
45631              * @param {HtmlEditor} this
45632              */
45633             autosave: true,
45634             /**
45635              * @event savedpreview
45636              * preview the saved version of htmlEditor
45637              * @param {HtmlEditor} this
45638              */
45639             savedpreview: true,
45640             
45641             /**
45642             * @event stylesheetsclick
45643             * Fires when press the Sytlesheets button
45644             * @param {Roo.HtmlEditorCore} this
45645             */
45646             stylesheetsclick: true
45647         });
45648         this.defaultAutoCreate =  {
45649             tag: "textarea",
45650             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45651             autocomplete: "new-password"
45652         };
45653     },
45654
45655     /**
45656      * Protected method that will not generally be called directly. It
45657      * is called when the editor creates its toolbar. Override this method if you need to
45658      * add custom toolbar buttons.
45659      * @param {HtmlEditor} editor
45660      */
45661     createToolbar : function(editor){
45662         Roo.log("create toolbars");
45663         if (!editor.toolbars || !editor.toolbars.length) {
45664             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45665         }
45666         
45667         for (var i =0 ; i < editor.toolbars.length;i++) {
45668             editor.toolbars[i] = Roo.factory(
45669                     typeof(editor.toolbars[i]) == 'string' ?
45670                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45671                 Roo.form.HtmlEditor);
45672             editor.toolbars[i].init(editor);
45673         }
45674          
45675         
45676     },
45677
45678      
45679     // private
45680     onRender : function(ct, position)
45681     {
45682         var _t = this;
45683         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45684         
45685         this.wrap = this.el.wrap({
45686             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45687         });
45688         
45689         this.editorcore.onRender(ct, position);
45690          
45691         if (this.resizable) {
45692             this.resizeEl = new Roo.Resizable(this.wrap, {
45693                 pinned : true,
45694                 wrap: true,
45695                 dynamic : true,
45696                 minHeight : this.height,
45697                 height: this.height,
45698                 handles : this.resizable,
45699                 width: this.width,
45700                 listeners : {
45701                     resize : function(r, w, h) {
45702                         _t.onResize(w,h); // -something
45703                     }
45704                 }
45705             });
45706             
45707         }
45708         this.createToolbar(this);
45709        
45710         
45711         if(!this.width){
45712             this.setSize(this.wrap.getSize());
45713         }
45714         if (this.resizeEl) {
45715             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45716             // should trigger onReize..
45717         }
45718         
45719         this.keyNav = new Roo.KeyNav(this.el, {
45720             
45721             "tab" : function(e){
45722                 e.preventDefault();
45723                 
45724                 var value = this.getValue();
45725                 
45726                 var start = this.el.dom.selectionStart;
45727                 var end = this.el.dom.selectionEnd;
45728                 
45729                 if(!e.shiftKey){
45730                     
45731                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45732                     this.el.dom.setSelectionRange(end + 1, end + 1);
45733                     return;
45734                 }
45735                 
45736                 var f = value.substring(0, start).split("\t");
45737                 
45738                 if(f.pop().length != 0){
45739                     return;
45740                 }
45741                 
45742                 this.setValue(f.join("\t") + value.substring(end));
45743                 this.el.dom.setSelectionRange(start - 1, start - 1);
45744                 
45745             },
45746             
45747             "home" : function(e){
45748                 e.preventDefault();
45749                 
45750                 var curr = this.el.dom.selectionStart;
45751                 var lines = this.getValue().split("\n");
45752                 
45753                 if(!lines.length){
45754                     return;
45755                 }
45756                 
45757                 if(e.ctrlKey){
45758                     this.el.dom.setSelectionRange(0, 0);
45759                     return;
45760                 }
45761                 
45762                 var pos = 0;
45763                 
45764                 for (var i = 0; i < lines.length;i++) {
45765                     pos += lines[i].length;
45766                     
45767                     if(i != 0){
45768                         pos += 1;
45769                     }
45770                     
45771                     if(pos < curr){
45772                         continue;
45773                     }
45774                     
45775                     pos -= lines[i].length;
45776                     
45777                     break;
45778                 }
45779                 
45780                 if(!e.shiftKey){
45781                     this.el.dom.setSelectionRange(pos, pos);
45782                     return;
45783                 }
45784                 
45785                 this.el.dom.selectionStart = pos;
45786                 this.el.dom.selectionEnd = curr;
45787             },
45788             
45789             "end" : function(e){
45790                 e.preventDefault();
45791                 
45792                 var curr = this.el.dom.selectionStart;
45793                 var lines = this.getValue().split("\n");
45794                 
45795                 if(!lines.length){
45796                     return;
45797                 }
45798                 
45799                 if(e.ctrlKey){
45800                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45801                     return;
45802                 }
45803                 
45804                 var pos = 0;
45805                 
45806                 for (var i = 0; i < lines.length;i++) {
45807                     
45808                     pos += lines[i].length;
45809                     
45810                     if(i != 0){
45811                         pos += 1;
45812                     }
45813                     
45814                     if(pos < curr){
45815                         continue;
45816                     }
45817                     
45818                     break;
45819                 }
45820                 
45821                 if(!e.shiftKey){
45822                     this.el.dom.setSelectionRange(pos, pos);
45823                     return;
45824                 }
45825                 
45826                 this.el.dom.selectionStart = curr;
45827                 this.el.dom.selectionEnd = pos;
45828             },
45829
45830             scope : this,
45831
45832             doRelay : function(foo, bar, hname){
45833                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45834             },
45835
45836             forceKeyDown: true
45837         });
45838         
45839 //        if(this.autosave && this.w){
45840 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45841 //        }
45842     },
45843
45844     // private
45845     onResize : function(w, h)
45846     {
45847         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45848         var ew = false;
45849         var eh = false;
45850         
45851         if(this.el ){
45852             if(typeof w == 'number'){
45853                 var aw = w - this.wrap.getFrameWidth('lr');
45854                 this.el.setWidth(this.adjustWidth('textarea', aw));
45855                 ew = aw;
45856             }
45857             if(typeof h == 'number'){
45858                 var tbh = 0;
45859                 for (var i =0; i < this.toolbars.length;i++) {
45860                     // fixme - ask toolbars for heights?
45861                     tbh += this.toolbars[i].tb.el.getHeight();
45862                     if (this.toolbars[i].footer) {
45863                         tbh += this.toolbars[i].footer.el.getHeight();
45864                     }
45865                 }
45866                 
45867                 
45868                 
45869                 
45870                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45871                 ah -= 5; // knock a few pixes off for look..
45872 //                Roo.log(ah);
45873                 this.el.setHeight(this.adjustWidth('textarea', ah));
45874                 var eh = ah;
45875             }
45876         }
45877         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45878         this.editorcore.onResize(ew,eh);
45879         
45880     },
45881
45882     /**
45883      * Toggles the editor between standard and source edit mode.
45884      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45885      */
45886     toggleSourceEdit : function(sourceEditMode)
45887     {
45888         this.editorcore.toggleSourceEdit(sourceEditMode);
45889         
45890         if(this.editorcore.sourceEditMode){
45891             Roo.log('editor - showing textarea');
45892             
45893 //            Roo.log('in');
45894 //            Roo.log(this.syncValue());
45895             this.editorcore.syncValue();
45896             this.el.removeClass('x-hidden');
45897             this.el.dom.removeAttribute('tabIndex');
45898             this.el.focus();
45899             
45900             for (var i = 0; i < this.toolbars.length; i++) {
45901                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45902                     this.toolbars[i].tb.hide();
45903                     this.toolbars[i].footer.hide();
45904                 }
45905             }
45906             
45907         }else{
45908             Roo.log('editor - hiding textarea');
45909 //            Roo.log('out')
45910 //            Roo.log(this.pushValue()); 
45911             this.editorcore.pushValue();
45912             
45913             this.el.addClass('x-hidden');
45914             this.el.dom.setAttribute('tabIndex', -1);
45915             
45916             for (var i = 0; i < this.toolbars.length; i++) {
45917                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45918                     this.toolbars[i].tb.show();
45919                     this.toolbars[i].footer.show();
45920                 }
45921             }
45922             
45923             //this.deferFocus();
45924         }
45925         
45926         this.setSize(this.wrap.getSize());
45927         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45928         
45929         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45930     },
45931  
45932     // private (for BoxComponent)
45933     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45934
45935     // private (for BoxComponent)
45936     getResizeEl : function(){
45937         return this.wrap;
45938     },
45939
45940     // private (for BoxComponent)
45941     getPositionEl : function(){
45942         return this.wrap;
45943     },
45944
45945     // private
45946     initEvents : function(){
45947         this.originalValue = this.getValue();
45948     },
45949
45950     /**
45951      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45952      * @method
45953      */
45954     markInvalid : Roo.emptyFn,
45955     /**
45956      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45957      * @method
45958      */
45959     clearInvalid : Roo.emptyFn,
45960
45961     setValue : function(v){
45962         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45963         this.editorcore.pushValue();
45964     },
45965
45966      
45967     // private
45968     deferFocus : function(){
45969         this.focus.defer(10, this);
45970     },
45971
45972     // doc'ed in Field
45973     focus : function(){
45974         this.editorcore.focus();
45975         
45976     },
45977       
45978
45979     // private
45980     onDestroy : function(){
45981         
45982         
45983         
45984         if(this.rendered){
45985             
45986             for (var i =0; i < this.toolbars.length;i++) {
45987                 // fixme - ask toolbars for heights?
45988                 this.toolbars[i].onDestroy();
45989             }
45990             
45991             this.wrap.dom.innerHTML = '';
45992             this.wrap.remove();
45993         }
45994     },
45995
45996     // private
45997     onFirstFocus : function(){
45998         //Roo.log("onFirstFocus");
45999         this.editorcore.onFirstFocus();
46000          for (var i =0; i < this.toolbars.length;i++) {
46001             this.toolbars[i].onFirstFocus();
46002         }
46003         
46004     },
46005     
46006     // private
46007     syncValue : function()
46008     {
46009         this.editorcore.syncValue();
46010     },
46011     
46012     pushValue : function()
46013     {
46014         this.editorcore.pushValue();
46015     },
46016     
46017     setStylesheets : function(stylesheets)
46018     {
46019         this.editorcore.setStylesheets(stylesheets);
46020     },
46021     
46022     removeStylesheets : function()
46023     {
46024         this.editorcore.removeStylesheets();
46025     }
46026      
46027     
46028     // hide stuff that is not compatible
46029     /**
46030      * @event blur
46031      * @hide
46032      */
46033     /**
46034      * @event change
46035      * @hide
46036      */
46037     /**
46038      * @event focus
46039      * @hide
46040      */
46041     /**
46042      * @event specialkey
46043      * @hide
46044      */
46045     /**
46046      * @cfg {String} fieldClass @hide
46047      */
46048     /**
46049      * @cfg {String} focusClass @hide
46050      */
46051     /**
46052      * @cfg {String} autoCreate @hide
46053      */
46054     /**
46055      * @cfg {String} inputType @hide
46056      */
46057     /**
46058      * @cfg {String} invalidClass @hide
46059      */
46060     /**
46061      * @cfg {String} invalidText @hide
46062      */
46063     /**
46064      * @cfg {String} msgFx @hide
46065      */
46066     /**
46067      * @cfg {String} validateOnBlur @hide
46068      */
46069 });
46070  
46071     // <script type="text/javascript">
46072 /*
46073  * Based on
46074  * Ext JS Library 1.1.1
46075  * Copyright(c) 2006-2007, Ext JS, LLC.
46076  *  
46077  
46078  */
46079
46080 /**
46081  * @class Roo.form.HtmlEditorToolbar1
46082  * Basic Toolbar
46083  * 
46084  * Usage:
46085  *
46086  new Roo.form.HtmlEditor({
46087     ....
46088     toolbars : [
46089         new Roo.form.HtmlEditorToolbar1({
46090             disable : { fonts: 1 , format: 1, ..., ... , ...],
46091             btns : [ .... ]
46092         })
46093     }
46094      
46095  * 
46096  * @cfg {Object} disable List of elements to disable..
46097  * @cfg {Array} btns List of additional buttons.
46098  * 
46099  * 
46100  * NEEDS Extra CSS? 
46101  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46102  */
46103  
46104 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46105 {
46106     
46107     Roo.apply(this, config);
46108     
46109     // default disabled, based on 'good practice'..
46110     this.disable = this.disable || {};
46111     Roo.applyIf(this.disable, {
46112         fontSize : true,
46113         colors : true,
46114         specialElements : true
46115     });
46116     
46117     
46118     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46119     // dont call parent... till later.
46120 }
46121
46122 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46123     
46124     tb: false,
46125     
46126     rendered: false,
46127     
46128     editor : false,
46129     editorcore : false,
46130     /**
46131      * @cfg {Object} disable  List of toolbar elements to disable
46132          
46133      */
46134     disable : false,
46135     
46136     
46137      /**
46138      * @cfg {String} createLinkText The default text for the create link prompt
46139      */
46140     createLinkText : 'Please enter the URL for the link:',
46141     /**
46142      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46143      */
46144     defaultLinkValue : 'http:/'+'/',
46145    
46146     
46147       /**
46148      * @cfg {Array} fontFamilies An array of available font families
46149      */
46150     fontFamilies : [
46151         'Arial',
46152         'Courier New',
46153         'Tahoma',
46154         'Times New Roman',
46155         'Verdana'
46156     ],
46157     
46158     specialChars : [
46159            "&#169;",
46160           "&#174;",     
46161           "&#8482;",    
46162           "&#163;" ,    
46163          // "&#8212;",    
46164           "&#8230;",    
46165           "&#247;" ,    
46166         //  "&#225;" ,     ?? a acute?
46167            "&#8364;"    , //Euro
46168        //   "&#8220;"    ,
46169         //  "&#8221;"    ,
46170         //  "&#8226;"    ,
46171           "&#176;"  //   , // degrees
46172
46173          // "&#233;"     , // e ecute
46174          // "&#250;"     , // u ecute?
46175     ],
46176     
46177     specialElements : [
46178         {
46179             text: "Insert Table",
46180             xtype: 'MenuItem',
46181             xns : Roo.Menu,
46182             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46183                 
46184         },
46185         {    
46186             text: "Insert Image",
46187             xtype: 'MenuItem',
46188             xns : Roo.Menu,
46189             ihtml : '<img src="about:blank"/>'
46190             
46191         }
46192         
46193          
46194     ],
46195     
46196     
46197     inputElements : [ 
46198             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46199             "input:submit", "input:button", "select", "textarea", "label" ],
46200     formats : [
46201         ["p"] ,  
46202         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46203         ["pre"],[ "code"], 
46204         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46205         ['div'],['span'],
46206         ['sup'],['sub']
46207     ],
46208     
46209     cleanStyles : [
46210         "font-size"
46211     ],
46212      /**
46213      * @cfg {String} defaultFont default font to use.
46214      */
46215     defaultFont: 'tahoma',
46216    
46217     fontSelect : false,
46218     
46219     
46220     formatCombo : false,
46221     
46222     init : function(editor)
46223     {
46224         this.editor = editor;
46225         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46226         var editorcore = this.editorcore;
46227         
46228         var _t = this;
46229         
46230         var fid = editorcore.frameId;
46231         var etb = this;
46232         function btn(id, toggle, handler){
46233             var xid = fid + '-'+ id ;
46234             return {
46235                 id : xid,
46236                 cmd : id,
46237                 cls : 'x-btn-icon x-edit-'+id,
46238                 enableToggle:toggle !== false,
46239                 scope: _t, // was editor...
46240                 handler:handler||_t.relayBtnCmd,
46241                 clickEvent:'mousedown',
46242                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46243                 tabIndex:-1
46244             };
46245         }
46246         
46247         
46248         
46249         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46250         this.tb = tb;
46251          // stop form submits
46252         tb.el.on('click', function(e){
46253             e.preventDefault(); // what does this do?
46254         });
46255
46256         if(!this.disable.font) { // && !Roo.isSafari){
46257             /* why no safari for fonts 
46258             editor.fontSelect = tb.el.createChild({
46259                 tag:'select',
46260                 tabIndex: -1,
46261                 cls:'x-font-select',
46262                 html: this.createFontOptions()
46263             });
46264             
46265             editor.fontSelect.on('change', function(){
46266                 var font = editor.fontSelect.dom.value;
46267                 editor.relayCmd('fontname', font);
46268                 editor.deferFocus();
46269             }, editor);
46270             
46271             tb.add(
46272                 editor.fontSelect.dom,
46273                 '-'
46274             );
46275             */
46276             
46277         };
46278         if(!this.disable.formats){
46279             this.formatCombo = new Roo.form.ComboBox({
46280                 store: new Roo.data.SimpleStore({
46281                     id : 'tag',
46282                     fields: ['tag'],
46283                     data : this.formats // from states.js
46284                 }),
46285                 blockFocus : true,
46286                 name : '',
46287                 //autoCreate : {tag: "div",  size: "20"},
46288                 displayField:'tag',
46289                 typeAhead: false,
46290                 mode: 'local',
46291                 editable : false,
46292                 triggerAction: 'all',
46293                 emptyText:'Add tag',
46294                 selectOnFocus:true,
46295                 width:135,
46296                 listeners : {
46297                     'select': function(c, r, i) {
46298                         editorcore.insertTag(r.get('tag'));
46299                         editor.focus();
46300                     }
46301                 }
46302
46303             });
46304             tb.addField(this.formatCombo);
46305             
46306         }
46307         
46308         if(!this.disable.format){
46309             tb.add(
46310                 btn('bold'),
46311                 btn('italic'),
46312                 btn('underline'),
46313                 btn('strikethrough')
46314             );
46315         };
46316         if(!this.disable.fontSize){
46317             tb.add(
46318                 '-',
46319                 
46320                 
46321                 btn('increasefontsize', false, editorcore.adjustFont),
46322                 btn('decreasefontsize', false, editorcore.adjustFont)
46323             );
46324         };
46325         
46326         
46327         if(!this.disable.colors){
46328             tb.add(
46329                 '-', {
46330                     id:editorcore.frameId +'-forecolor',
46331                     cls:'x-btn-icon x-edit-forecolor',
46332                     clickEvent:'mousedown',
46333                     tooltip: this.buttonTips['forecolor'] || undefined,
46334                     tabIndex:-1,
46335                     menu : new Roo.menu.ColorMenu({
46336                         allowReselect: true,
46337                         focus: Roo.emptyFn,
46338                         value:'000000',
46339                         plain:true,
46340                         selectHandler: function(cp, color){
46341                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46342                             editor.deferFocus();
46343                         },
46344                         scope: editorcore,
46345                         clickEvent:'mousedown'
46346                     })
46347                 }, {
46348                     id:editorcore.frameId +'backcolor',
46349                     cls:'x-btn-icon x-edit-backcolor',
46350                     clickEvent:'mousedown',
46351                     tooltip: this.buttonTips['backcolor'] || undefined,
46352                     tabIndex:-1,
46353                     menu : new Roo.menu.ColorMenu({
46354                         focus: Roo.emptyFn,
46355                         value:'FFFFFF',
46356                         plain:true,
46357                         allowReselect: true,
46358                         selectHandler: function(cp, color){
46359                             if(Roo.isGecko){
46360                                 editorcore.execCmd('useCSS', false);
46361                                 editorcore.execCmd('hilitecolor', color);
46362                                 editorcore.execCmd('useCSS', true);
46363                                 editor.deferFocus();
46364                             }else{
46365                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46366                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46367                                 editor.deferFocus();
46368                             }
46369                         },
46370                         scope:editorcore,
46371                         clickEvent:'mousedown'
46372                     })
46373                 }
46374             );
46375         };
46376         // now add all the items...
46377         
46378
46379         if(!this.disable.alignments){
46380             tb.add(
46381                 '-',
46382                 btn('justifyleft'),
46383                 btn('justifycenter'),
46384                 btn('justifyright')
46385             );
46386         };
46387
46388         //if(!Roo.isSafari){
46389             if(!this.disable.links){
46390                 tb.add(
46391                     '-',
46392                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46393                 );
46394             };
46395
46396             if(!this.disable.lists){
46397                 tb.add(
46398                     '-',
46399                     btn('insertorderedlist'),
46400                     btn('insertunorderedlist')
46401                 );
46402             }
46403             if(!this.disable.sourceEdit){
46404                 tb.add(
46405                     '-',
46406                     btn('sourceedit', true, function(btn){
46407                         this.toggleSourceEdit(btn.pressed);
46408                     })
46409                 );
46410             }
46411         //}
46412         
46413         var smenu = { };
46414         // special menu.. - needs to be tidied up..
46415         if (!this.disable.special) {
46416             smenu = {
46417                 text: "&#169;",
46418                 cls: 'x-edit-none',
46419                 
46420                 menu : {
46421                     items : []
46422                 }
46423             };
46424             for (var i =0; i < this.specialChars.length; i++) {
46425                 smenu.menu.items.push({
46426                     
46427                     html: this.specialChars[i],
46428                     handler: function(a,b) {
46429                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46430                         //editor.insertAtCursor(a.html);
46431                         
46432                     },
46433                     tabIndex:-1
46434                 });
46435             }
46436             
46437             
46438             tb.add(smenu);
46439             
46440             
46441         }
46442         
46443         var cmenu = { };
46444         if (!this.disable.cleanStyles) {
46445             cmenu = {
46446                 cls: 'x-btn-icon x-btn-clear',
46447                 
46448                 menu : {
46449                     items : []
46450                 }
46451             };
46452             for (var i =0; i < this.cleanStyles.length; i++) {
46453                 cmenu.menu.items.push({
46454                     actiontype : this.cleanStyles[i],
46455                     html: 'Remove ' + this.cleanStyles[i],
46456                     handler: function(a,b) {
46457 //                        Roo.log(a);
46458 //                        Roo.log(b);
46459                         var c = Roo.get(editorcore.doc.body);
46460                         c.select('[style]').each(function(s) {
46461                             s.dom.style.removeProperty(a.actiontype);
46462                         });
46463                         editorcore.syncValue();
46464                     },
46465                     tabIndex:-1
46466                 });
46467             }
46468              cmenu.menu.items.push({
46469                 actiontype : 'tablewidths',
46470                 html: 'Remove Table Widths',
46471                 handler: function(a,b) {
46472                     editorcore.cleanTableWidths();
46473                     editorcore.syncValue();
46474                 },
46475                 tabIndex:-1
46476             });
46477             cmenu.menu.items.push({
46478                 actiontype : 'word',
46479                 html: 'Remove MS Word Formating',
46480                 handler: function(a,b) {
46481                     editorcore.cleanWord();
46482                     editorcore.syncValue();
46483                 },
46484                 tabIndex:-1
46485             });
46486             
46487             cmenu.menu.items.push({
46488                 actiontype : 'all',
46489                 html: 'Remove All Styles',
46490                 handler: function(a,b) {
46491                     
46492                     var c = Roo.get(editorcore.doc.body);
46493                     c.select('[style]').each(function(s) {
46494                         s.dom.removeAttribute('style');
46495                     });
46496                     editorcore.syncValue();
46497                 },
46498                 tabIndex:-1
46499             });
46500             
46501             cmenu.menu.items.push({
46502                 actiontype : 'all',
46503                 html: 'Remove All CSS Classes',
46504                 handler: function(a,b) {
46505                     
46506                     var c = Roo.get(editorcore.doc.body);
46507                     c.select('[class]').each(function(s) {
46508                         s.dom.removeAttribute('class');
46509                     });
46510                     editorcore.cleanWord();
46511                     editorcore.syncValue();
46512                 },
46513                 tabIndex:-1
46514             });
46515             
46516              cmenu.menu.items.push({
46517                 actiontype : 'tidy',
46518                 html: 'Tidy HTML Source',
46519                 handler: function(a,b) {
46520                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46521                     editorcore.syncValue();
46522                 },
46523                 tabIndex:-1
46524             });
46525             
46526             
46527             tb.add(cmenu);
46528         }
46529          
46530         if (!this.disable.specialElements) {
46531             var semenu = {
46532                 text: "Other;",
46533                 cls: 'x-edit-none',
46534                 menu : {
46535                     items : []
46536                 }
46537             };
46538             for (var i =0; i < this.specialElements.length; i++) {
46539                 semenu.menu.items.push(
46540                     Roo.apply({ 
46541                         handler: function(a,b) {
46542                             editor.insertAtCursor(this.ihtml);
46543                         }
46544                     }, this.specialElements[i])
46545                 );
46546                     
46547             }
46548             
46549             tb.add(semenu);
46550             
46551             
46552         }
46553          
46554         
46555         if (this.btns) {
46556             for(var i =0; i< this.btns.length;i++) {
46557                 var b = Roo.factory(this.btns[i],Roo.form);
46558                 b.cls =  'x-edit-none';
46559                 
46560                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46561                     b.cls += ' x-init-enable';
46562                 }
46563                 
46564                 b.scope = editorcore;
46565                 tb.add(b);
46566             }
46567         
46568         }
46569         
46570         
46571         
46572         // disable everything...
46573         
46574         this.tb.items.each(function(item){
46575             
46576            if(
46577                 item.id != editorcore.frameId+ '-sourceedit' && 
46578                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46579             ){
46580                 
46581                 item.disable();
46582             }
46583         });
46584         this.rendered = true;
46585         
46586         // the all the btns;
46587         editor.on('editorevent', this.updateToolbar, this);
46588         // other toolbars need to implement this..
46589         //editor.on('editmodechange', this.updateToolbar, this);
46590     },
46591     
46592     
46593     relayBtnCmd : function(btn) {
46594         this.editorcore.relayCmd(btn.cmd);
46595     },
46596     // private used internally
46597     createLink : function(){
46598         Roo.log("create link?");
46599         var url = prompt(this.createLinkText, this.defaultLinkValue);
46600         if(url && url != 'http:/'+'/'){
46601             this.editorcore.relayCmd('createlink', url);
46602         }
46603     },
46604
46605     
46606     /**
46607      * Protected method that will not generally be called directly. It triggers
46608      * a toolbar update by reading the markup state of the current selection in the editor.
46609      */
46610     updateToolbar: function(){
46611
46612         if(!this.editorcore.activated){
46613             this.editor.onFirstFocus();
46614             return;
46615         }
46616
46617         var btns = this.tb.items.map, 
46618             doc = this.editorcore.doc,
46619             frameId = this.editorcore.frameId;
46620
46621         if(!this.disable.font && !Roo.isSafari){
46622             /*
46623             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46624             if(name != this.fontSelect.dom.value){
46625                 this.fontSelect.dom.value = name;
46626             }
46627             */
46628         }
46629         if(!this.disable.format){
46630             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46631             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46632             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46633             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46634         }
46635         if(!this.disable.alignments){
46636             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46637             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46638             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46639         }
46640         if(!Roo.isSafari && !this.disable.lists){
46641             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46642             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46643         }
46644         
46645         var ans = this.editorcore.getAllAncestors();
46646         if (this.formatCombo) {
46647             
46648             
46649             var store = this.formatCombo.store;
46650             this.formatCombo.setValue("");
46651             for (var i =0; i < ans.length;i++) {
46652                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46653                     // select it..
46654                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46655                     break;
46656                 }
46657             }
46658         }
46659         
46660         
46661         
46662         // hides menus... - so this cant be on a menu...
46663         Roo.menu.MenuMgr.hideAll();
46664
46665         //this.editorsyncValue();
46666     },
46667    
46668     
46669     createFontOptions : function(){
46670         var buf = [], fs = this.fontFamilies, ff, lc;
46671         
46672         
46673         
46674         for(var i = 0, len = fs.length; i< len; i++){
46675             ff = fs[i];
46676             lc = ff.toLowerCase();
46677             buf.push(
46678                 '<option value="',lc,'" style="font-family:',ff,';"',
46679                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46680                     ff,
46681                 '</option>'
46682             );
46683         }
46684         return buf.join('');
46685     },
46686     
46687     toggleSourceEdit : function(sourceEditMode){
46688         
46689         Roo.log("toolbar toogle");
46690         if(sourceEditMode === undefined){
46691             sourceEditMode = !this.sourceEditMode;
46692         }
46693         this.sourceEditMode = sourceEditMode === true;
46694         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46695         // just toggle the button?
46696         if(btn.pressed !== this.sourceEditMode){
46697             btn.toggle(this.sourceEditMode);
46698             return;
46699         }
46700         
46701         if(sourceEditMode){
46702             Roo.log("disabling buttons");
46703             this.tb.items.each(function(item){
46704                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46705                     item.disable();
46706                 }
46707             });
46708           
46709         }else{
46710             Roo.log("enabling buttons");
46711             if(this.editorcore.initialized){
46712                 this.tb.items.each(function(item){
46713                     item.enable();
46714                 });
46715             }
46716             
46717         }
46718         Roo.log("calling toggole on editor");
46719         // tell the editor that it's been pressed..
46720         this.editor.toggleSourceEdit(sourceEditMode);
46721        
46722     },
46723      /**
46724      * Object collection of toolbar tooltips for the buttons in the editor. The key
46725      * is the command id associated with that button and the value is a valid QuickTips object.
46726      * For example:
46727 <pre><code>
46728 {
46729     bold : {
46730         title: 'Bold (Ctrl+B)',
46731         text: 'Make the selected text bold.',
46732         cls: 'x-html-editor-tip'
46733     },
46734     italic : {
46735         title: 'Italic (Ctrl+I)',
46736         text: 'Make the selected text italic.',
46737         cls: 'x-html-editor-tip'
46738     },
46739     ...
46740 </code></pre>
46741     * @type Object
46742      */
46743     buttonTips : {
46744         bold : {
46745             title: 'Bold (Ctrl+B)',
46746             text: 'Make the selected text bold.',
46747             cls: 'x-html-editor-tip'
46748         },
46749         italic : {
46750             title: 'Italic (Ctrl+I)',
46751             text: 'Make the selected text italic.',
46752             cls: 'x-html-editor-tip'
46753         },
46754         underline : {
46755             title: 'Underline (Ctrl+U)',
46756             text: 'Underline the selected text.',
46757             cls: 'x-html-editor-tip'
46758         },
46759         strikethrough : {
46760             title: 'Strikethrough',
46761             text: 'Strikethrough the selected text.',
46762             cls: 'x-html-editor-tip'
46763         },
46764         increasefontsize : {
46765             title: 'Grow Text',
46766             text: 'Increase the font size.',
46767             cls: 'x-html-editor-tip'
46768         },
46769         decreasefontsize : {
46770             title: 'Shrink Text',
46771             text: 'Decrease the font size.',
46772             cls: 'x-html-editor-tip'
46773         },
46774         backcolor : {
46775             title: 'Text Highlight Color',
46776             text: 'Change the background color of the selected text.',
46777             cls: 'x-html-editor-tip'
46778         },
46779         forecolor : {
46780             title: 'Font Color',
46781             text: 'Change the color of the selected text.',
46782             cls: 'x-html-editor-tip'
46783         },
46784         justifyleft : {
46785             title: 'Align Text Left',
46786             text: 'Align text to the left.',
46787             cls: 'x-html-editor-tip'
46788         },
46789         justifycenter : {
46790             title: 'Center Text',
46791             text: 'Center text in the editor.',
46792             cls: 'x-html-editor-tip'
46793         },
46794         justifyright : {
46795             title: 'Align Text Right',
46796             text: 'Align text to the right.',
46797             cls: 'x-html-editor-tip'
46798         },
46799         insertunorderedlist : {
46800             title: 'Bullet List',
46801             text: 'Start a bulleted list.',
46802             cls: 'x-html-editor-tip'
46803         },
46804         insertorderedlist : {
46805             title: 'Numbered List',
46806             text: 'Start a numbered list.',
46807             cls: 'x-html-editor-tip'
46808         },
46809         createlink : {
46810             title: 'Hyperlink',
46811             text: 'Make the selected text a hyperlink.',
46812             cls: 'x-html-editor-tip'
46813         },
46814         sourceedit : {
46815             title: 'Source Edit',
46816             text: 'Switch to source editing mode.',
46817             cls: 'x-html-editor-tip'
46818         }
46819     },
46820     // private
46821     onDestroy : function(){
46822         if(this.rendered){
46823             
46824             this.tb.items.each(function(item){
46825                 if(item.menu){
46826                     item.menu.removeAll();
46827                     if(item.menu.el){
46828                         item.menu.el.destroy();
46829                     }
46830                 }
46831                 item.destroy();
46832             });
46833              
46834         }
46835     },
46836     onFirstFocus: function() {
46837         this.tb.items.each(function(item){
46838            item.enable();
46839         });
46840     }
46841 });
46842
46843
46844
46845
46846 // <script type="text/javascript">
46847 /*
46848  * Based on
46849  * Ext JS Library 1.1.1
46850  * Copyright(c) 2006-2007, Ext JS, LLC.
46851  *  
46852  
46853  */
46854
46855  
46856 /**
46857  * @class Roo.form.HtmlEditor.ToolbarContext
46858  * Context Toolbar
46859  * 
46860  * Usage:
46861  *
46862  new Roo.form.HtmlEditor({
46863     ....
46864     toolbars : [
46865         { xtype: 'ToolbarStandard', styles : {} }
46866         { xtype: 'ToolbarContext', disable : {} }
46867     ]
46868 })
46869
46870      
46871  * 
46872  * @config : {Object} disable List of elements to disable.. (not done yet.)
46873  * @config : {Object} styles  Map of styles available.
46874  * 
46875  */
46876
46877 Roo.form.HtmlEditor.ToolbarContext = function(config)
46878 {
46879     
46880     Roo.apply(this, config);
46881     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46882     // dont call parent... till later.
46883     this.styles = this.styles || {};
46884 }
46885
46886  
46887
46888 Roo.form.HtmlEditor.ToolbarContext.types = {
46889     'IMG' : {
46890         width : {
46891             title: "Width",
46892             width: 40
46893         },
46894         height:  {
46895             title: "Height",
46896             width: 40
46897         },
46898         align: {
46899             title: "Align",
46900             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46901             width : 80
46902             
46903         },
46904         border: {
46905             title: "Border",
46906             width: 40
46907         },
46908         alt: {
46909             title: "Alt",
46910             width: 120
46911         },
46912         src : {
46913             title: "Src",
46914             width: 220
46915         }
46916         
46917     },
46918     'A' : {
46919         name : {
46920             title: "Name",
46921             width: 50
46922         },
46923         target:  {
46924             title: "Target",
46925             width: 120
46926         },
46927         href:  {
46928             title: "Href",
46929             width: 220
46930         } // border?
46931         
46932     },
46933     'TABLE' : {
46934         rows : {
46935             title: "Rows",
46936             width: 20
46937         },
46938         cols : {
46939             title: "Cols",
46940             width: 20
46941         },
46942         width : {
46943             title: "Width",
46944             width: 40
46945         },
46946         height : {
46947             title: "Height",
46948             width: 40
46949         },
46950         border : {
46951             title: "Border",
46952             width: 20
46953         }
46954     },
46955     'TD' : {
46956         width : {
46957             title: "Width",
46958             width: 40
46959         },
46960         height : {
46961             title: "Height",
46962             width: 40
46963         },   
46964         align: {
46965             title: "Align",
46966             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46967             width: 80
46968         },
46969         valign: {
46970             title: "Valign",
46971             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46972             width: 80
46973         },
46974         colspan: {
46975             title: "Colspan",
46976             width: 20
46977             
46978         },
46979          'font-family'  : {
46980             title : "Font",
46981             style : 'fontFamily',
46982             displayField: 'display',
46983             optname : 'font-family',
46984             width: 140
46985         }
46986     },
46987     'INPUT' : {
46988         name : {
46989             title: "name",
46990             width: 120
46991         },
46992         value : {
46993             title: "Value",
46994             width: 120
46995         },
46996         width : {
46997             title: "Width",
46998             width: 40
46999         }
47000     },
47001     'LABEL' : {
47002         'for' : {
47003             title: "For",
47004             width: 120
47005         }
47006     },
47007     'TEXTAREA' : {
47008           name : {
47009             title: "name",
47010             width: 120
47011         },
47012         rows : {
47013             title: "Rows",
47014             width: 20
47015         },
47016         cols : {
47017             title: "Cols",
47018             width: 20
47019         }
47020     },
47021     'SELECT' : {
47022         name : {
47023             title: "name",
47024             width: 120
47025         },
47026         selectoptions : {
47027             title: "Options",
47028             width: 200
47029         }
47030     },
47031     
47032     // should we really allow this??
47033     // should this just be 
47034     'BODY' : {
47035         title : {
47036             title: "Title",
47037             width: 200,
47038             disabled : true
47039         }
47040     },
47041     'SPAN' : {
47042         'font-family'  : {
47043             title : "Font",
47044             style : 'fontFamily',
47045             displayField: 'display',
47046             optname : 'font-family',
47047             width: 140
47048         }
47049     },
47050     'DIV' : {
47051         'font-family'  : {
47052             title : "Font",
47053             style : 'fontFamily',
47054             displayField: 'display',
47055             optname : 'font-family',
47056             width: 140
47057         }
47058     },
47059      'P' : {
47060         'font-family'  : {
47061             title : "Font",
47062             style : 'fontFamily',
47063             displayField: 'display',
47064             optname : 'font-family',
47065             width: 140
47066         }
47067     },
47068     
47069     '*' : {
47070         // empty..
47071     }
47072
47073 };
47074
47075 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47076 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47077
47078 Roo.form.HtmlEditor.ToolbarContext.options = {
47079         'font-family'  : [ 
47080                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47081                 [ 'Courier New', 'Courier New'],
47082                 [ 'Tahoma', 'Tahoma'],
47083                 [ 'Times New Roman,serif', 'Times'],
47084                 [ 'Verdana','Verdana' ]
47085         ]
47086 };
47087
47088 // fixme - these need to be configurable..
47089  
47090
47091 //Roo.form.HtmlEditor.ToolbarContext.types
47092
47093
47094 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47095     
47096     tb: false,
47097     
47098     rendered: false,
47099     
47100     editor : false,
47101     editorcore : false,
47102     /**
47103      * @cfg {Object} disable  List of toolbar elements to disable
47104          
47105      */
47106     disable : false,
47107     /**
47108      * @cfg {Object} styles List of styles 
47109      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47110      *
47111      * These must be defined in the page, so they get rendered correctly..
47112      * .headline { }
47113      * TD.underline { }
47114      * 
47115      */
47116     styles : false,
47117     
47118     options: false,
47119     
47120     toolbars : false,
47121     
47122     init : function(editor)
47123     {
47124         this.editor = editor;
47125         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47126         var editorcore = this.editorcore;
47127         
47128         var fid = editorcore.frameId;
47129         var etb = this;
47130         function btn(id, toggle, handler){
47131             var xid = fid + '-'+ id ;
47132             return {
47133                 id : xid,
47134                 cmd : id,
47135                 cls : 'x-btn-icon x-edit-'+id,
47136                 enableToggle:toggle !== false,
47137                 scope: editorcore, // was editor...
47138                 handler:handler||editorcore.relayBtnCmd,
47139                 clickEvent:'mousedown',
47140                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47141                 tabIndex:-1
47142             };
47143         }
47144         // create a new element.
47145         var wdiv = editor.wrap.createChild({
47146                 tag: 'div'
47147             }, editor.wrap.dom.firstChild.nextSibling, true);
47148         
47149         // can we do this more than once??
47150         
47151          // stop form submits
47152       
47153  
47154         // disable everything...
47155         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47156         this.toolbars = {};
47157            
47158         for (var i in  ty) {
47159           
47160             this.toolbars[i] = this.buildToolbar(ty[i],i);
47161         }
47162         this.tb = this.toolbars.BODY;
47163         this.tb.el.show();
47164         this.buildFooter();
47165         this.footer.show();
47166         editor.on('hide', function( ) { this.footer.hide() }, this);
47167         editor.on('show', function( ) { this.footer.show() }, this);
47168         
47169          
47170         this.rendered = true;
47171         
47172         // the all the btns;
47173         editor.on('editorevent', this.updateToolbar, this);
47174         // other toolbars need to implement this..
47175         //editor.on('editmodechange', this.updateToolbar, this);
47176     },
47177     
47178     
47179     
47180     /**
47181      * Protected method that will not generally be called directly. It triggers
47182      * a toolbar update by reading the markup state of the current selection in the editor.
47183      *
47184      * Note you can force an update by calling on('editorevent', scope, false)
47185      */
47186     updateToolbar: function(editor,ev,sel){
47187
47188         //Roo.log(ev);
47189         // capture mouse up - this is handy for selecting images..
47190         // perhaps should go somewhere else...
47191         if(!this.editorcore.activated){
47192              this.editor.onFirstFocus();
47193             return;
47194         }
47195         
47196         
47197         
47198         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47199         // selectNode - might want to handle IE?
47200         if (ev &&
47201             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47202             ev.target && ev.target.tagName == 'IMG') {
47203             // they have click on an image...
47204             // let's see if we can change the selection...
47205             sel = ev.target;
47206          
47207               var nodeRange = sel.ownerDocument.createRange();
47208             try {
47209                 nodeRange.selectNode(sel);
47210             } catch (e) {
47211                 nodeRange.selectNodeContents(sel);
47212             }
47213             //nodeRange.collapse(true);
47214             var s = this.editorcore.win.getSelection();
47215             s.removeAllRanges();
47216             s.addRange(nodeRange);
47217         }  
47218         
47219       
47220         var updateFooter = sel ? false : true;
47221         
47222         
47223         var ans = this.editorcore.getAllAncestors();
47224         
47225         // pick
47226         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47227         
47228         if (!sel) { 
47229             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47230             sel = sel ? sel : this.editorcore.doc.body;
47231             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47232             
47233         }
47234         // pick a menu that exists..
47235         var tn = sel.tagName.toUpperCase();
47236         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47237         
47238         tn = sel.tagName.toUpperCase();
47239         
47240         var lastSel = this.tb.selectedNode;
47241         
47242         this.tb.selectedNode = sel;
47243         
47244         // if current menu does not match..
47245         
47246         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47247                 
47248             this.tb.el.hide();
47249             ///console.log("show: " + tn);
47250             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47251             this.tb.el.show();
47252             // update name
47253             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47254             
47255             
47256             // update attributes
47257             if (this.tb.fields) {
47258                 this.tb.fields.each(function(e) {
47259                     if (e.stylename) {
47260                         e.setValue(sel.style[e.stylename]);
47261                         return;
47262                     } 
47263                    e.setValue(sel.getAttribute(e.attrname));
47264                 });
47265             }
47266             
47267             var hasStyles = false;
47268             for(var i in this.styles) {
47269                 hasStyles = true;
47270                 break;
47271             }
47272             
47273             // update styles
47274             if (hasStyles) { 
47275                 var st = this.tb.fields.item(0);
47276                 
47277                 st.store.removeAll();
47278                
47279                 
47280                 var cn = sel.className.split(/\s+/);
47281                 
47282                 var avs = [];
47283                 if (this.styles['*']) {
47284                     
47285                     Roo.each(this.styles['*'], function(v) {
47286                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47287                     });
47288                 }
47289                 if (this.styles[tn]) { 
47290                     Roo.each(this.styles[tn], function(v) {
47291                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47292                     });
47293                 }
47294                 
47295                 st.store.loadData(avs);
47296                 st.collapse();
47297                 st.setValue(cn);
47298             }
47299             // flag our selected Node.
47300             this.tb.selectedNode = sel;
47301            
47302            
47303             Roo.menu.MenuMgr.hideAll();
47304
47305         }
47306         
47307         if (!updateFooter) {
47308             //this.footDisp.dom.innerHTML = ''; 
47309             return;
47310         }
47311         // update the footer
47312         //
47313         var html = '';
47314         
47315         this.footerEls = ans.reverse();
47316         Roo.each(this.footerEls, function(a,i) {
47317             if (!a) { return; }
47318             html += html.length ? ' &gt; '  :  '';
47319             
47320             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47321             
47322         });
47323        
47324         // 
47325         var sz = this.footDisp.up('td').getSize();
47326         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47327         this.footDisp.dom.style.marginLeft = '5px';
47328         
47329         this.footDisp.dom.style.overflow = 'hidden';
47330         
47331         this.footDisp.dom.innerHTML = html;
47332             
47333         //this.editorsyncValue();
47334     },
47335      
47336     
47337    
47338        
47339     // private
47340     onDestroy : function(){
47341         if(this.rendered){
47342             
47343             this.tb.items.each(function(item){
47344                 if(item.menu){
47345                     item.menu.removeAll();
47346                     if(item.menu.el){
47347                         item.menu.el.destroy();
47348                     }
47349                 }
47350                 item.destroy();
47351             });
47352              
47353         }
47354     },
47355     onFirstFocus: function() {
47356         // need to do this for all the toolbars..
47357         this.tb.items.each(function(item){
47358            item.enable();
47359         });
47360     },
47361     buildToolbar: function(tlist, nm)
47362     {
47363         var editor = this.editor;
47364         var editorcore = this.editorcore;
47365          // create a new element.
47366         var wdiv = editor.wrap.createChild({
47367                 tag: 'div'
47368             }, editor.wrap.dom.firstChild.nextSibling, true);
47369         
47370        
47371         var tb = new Roo.Toolbar(wdiv);
47372         // add the name..
47373         
47374         tb.add(nm+ ":&nbsp;");
47375         
47376         var styles = [];
47377         for(var i in this.styles) {
47378             styles.push(i);
47379         }
47380         
47381         // styles...
47382         if (styles && styles.length) {
47383             
47384             // this needs a multi-select checkbox...
47385             tb.addField( new Roo.form.ComboBox({
47386                 store: new Roo.data.SimpleStore({
47387                     id : 'val',
47388                     fields: ['val', 'selected'],
47389                     data : [] 
47390                 }),
47391                 name : '-roo-edit-className',
47392                 attrname : 'className',
47393                 displayField: 'val',
47394                 typeAhead: false,
47395                 mode: 'local',
47396                 editable : false,
47397                 triggerAction: 'all',
47398                 emptyText:'Select Style',
47399                 selectOnFocus:true,
47400                 width: 130,
47401                 listeners : {
47402                     'select': function(c, r, i) {
47403                         // initial support only for on class per el..
47404                         tb.selectedNode.className =  r ? r.get('val') : '';
47405                         editorcore.syncValue();
47406                     }
47407                 }
47408     
47409             }));
47410         }
47411         
47412         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47413         var tbops = tbc.options;
47414         
47415         for (var i in tlist) {
47416             
47417             var item = tlist[i];
47418             tb.add(item.title + ":&nbsp;");
47419             
47420             
47421             //optname == used so you can configure the options available..
47422             var opts = item.opts ? item.opts : false;
47423             if (item.optname) {
47424                 opts = tbops[item.optname];
47425            
47426             }
47427             
47428             if (opts) {
47429                 // opts == pulldown..
47430                 tb.addField( new Roo.form.ComboBox({
47431                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47432                         id : 'val',
47433                         fields: ['val', 'display'],
47434                         data : opts  
47435                     }),
47436                     name : '-roo-edit-' + i,
47437                     attrname : i,
47438                     stylename : item.style ? item.style : false,
47439                     displayField: item.displayField ? item.displayField : 'val',
47440                     valueField :  'val',
47441                     typeAhead: false,
47442                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47443                     editable : false,
47444                     triggerAction: 'all',
47445                     emptyText:'Select',
47446                     selectOnFocus:true,
47447                     width: item.width ? item.width  : 130,
47448                     listeners : {
47449                         'select': function(c, r, i) {
47450                             if (c.stylename) {
47451                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47452                                 return;
47453                             }
47454                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47455                         }
47456                     }
47457
47458                 }));
47459                 continue;
47460                     
47461                  
47462                 
47463                 tb.addField( new Roo.form.TextField({
47464                     name: i,
47465                     width: 100,
47466                     //allowBlank:false,
47467                     value: ''
47468                 }));
47469                 continue;
47470             }
47471             tb.addField( new Roo.form.TextField({
47472                 name: '-roo-edit-' + i,
47473                 attrname : i,
47474                 
47475                 width: item.width,
47476                 //allowBlank:true,
47477                 value: '',
47478                 listeners: {
47479                     'change' : function(f, nv, ov) {
47480                         tb.selectedNode.setAttribute(f.attrname, nv);
47481                         editorcore.syncValue();
47482                     }
47483                 }
47484             }));
47485              
47486         }
47487         
47488         var _this = this;
47489         
47490         if(nm == 'BODY'){
47491             tb.addSeparator();
47492         
47493             tb.addButton( {
47494                 text: 'Stylesheets',
47495
47496                 listeners : {
47497                     click : function ()
47498                     {
47499                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47500                     }
47501                 }
47502             });
47503         }
47504         
47505         tb.addFill();
47506         tb.addButton( {
47507             text: 'Remove Tag',
47508     
47509             listeners : {
47510                 click : function ()
47511                 {
47512                     // remove
47513                     // undo does not work.
47514                      
47515                     var sn = tb.selectedNode;
47516                     
47517                     var pn = sn.parentNode;
47518                     
47519                     var stn =  sn.childNodes[0];
47520                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47521                     while (sn.childNodes.length) {
47522                         var node = sn.childNodes[0];
47523                         sn.removeChild(node);
47524                         //Roo.log(node);
47525                         pn.insertBefore(node, sn);
47526                         
47527                     }
47528                     pn.removeChild(sn);
47529                     var range = editorcore.createRange();
47530         
47531                     range.setStart(stn,0);
47532                     range.setEnd(en,0); //????
47533                     //range.selectNode(sel);
47534                     
47535                     
47536                     var selection = editorcore.getSelection();
47537                     selection.removeAllRanges();
47538                     selection.addRange(range);
47539                     
47540                     
47541                     
47542                     //_this.updateToolbar(null, null, pn);
47543                     _this.updateToolbar(null, null, null);
47544                     _this.footDisp.dom.innerHTML = ''; 
47545                 }
47546             }
47547             
47548                     
47549                 
47550             
47551         });
47552         
47553         
47554         tb.el.on('click', function(e){
47555             e.preventDefault(); // what does this do?
47556         });
47557         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47558         tb.el.hide();
47559         tb.name = nm;
47560         // dont need to disable them... as they will get hidden
47561         return tb;
47562          
47563         
47564     },
47565     buildFooter : function()
47566     {
47567         
47568         var fel = this.editor.wrap.createChild();
47569         this.footer = new Roo.Toolbar(fel);
47570         // toolbar has scrolly on left / right?
47571         var footDisp= new Roo.Toolbar.Fill();
47572         var _t = this;
47573         this.footer.add(
47574             {
47575                 text : '&lt;',
47576                 xtype: 'Button',
47577                 handler : function() {
47578                     _t.footDisp.scrollTo('left',0,true)
47579                 }
47580             }
47581         );
47582         this.footer.add( footDisp );
47583         this.footer.add( 
47584             {
47585                 text : '&gt;',
47586                 xtype: 'Button',
47587                 handler : function() {
47588                     // no animation..
47589                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47590                 }
47591             }
47592         );
47593         var fel = Roo.get(footDisp.el);
47594         fel.addClass('x-editor-context');
47595         this.footDispWrap = fel; 
47596         this.footDispWrap.overflow  = 'hidden';
47597         
47598         this.footDisp = fel.createChild();
47599         this.footDispWrap.on('click', this.onContextClick, this)
47600         
47601         
47602     },
47603     onContextClick : function (ev,dom)
47604     {
47605         ev.preventDefault();
47606         var  cn = dom.className;
47607         //Roo.log(cn);
47608         if (!cn.match(/x-ed-loc-/)) {
47609             return;
47610         }
47611         var n = cn.split('-').pop();
47612         var ans = this.footerEls;
47613         var sel = ans[n];
47614         
47615          // pick
47616         var range = this.editorcore.createRange();
47617         
47618         range.selectNodeContents(sel);
47619         //range.selectNode(sel);
47620         
47621         
47622         var selection = this.editorcore.getSelection();
47623         selection.removeAllRanges();
47624         selection.addRange(range);
47625         
47626         
47627         
47628         this.updateToolbar(null, null, sel);
47629         
47630         
47631     }
47632     
47633     
47634     
47635     
47636     
47637 });
47638
47639
47640
47641
47642
47643 /*
47644  * Based on:
47645  * Ext JS Library 1.1.1
47646  * Copyright(c) 2006-2007, Ext JS, LLC.
47647  *
47648  * Originally Released Under LGPL - original licence link has changed is not relivant.
47649  *
47650  * Fork - LGPL
47651  * <script type="text/javascript">
47652  */
47653  
47654 /**
47655  * @class Roo.form.BasicForm
47656  * @extends Roo.util.Observable
47657  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47658  * @constructor
47659  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47660  * @param {Object} config Configuration options
47661  */
47662 Roo.form.BasicForm = function(el, config){
47663     this.allItems = [];
47664     this.childForms = [];
47665     Roo.apply(this, config);
47666     /*
47667      * The Roo.form.Field items in this form.
47668      * @type MixedCollection
47669      */
47670      
47671      
47672     this.items = new Roo.util.MixedCollection(false, function(o){
47673         return o.id || (o.id = Roo.id());
47674     });
47675     this.addEvents({
47676         /**
47677          * @event beforeaction
47678          * Fires before any action is performed. Return false to cancel the action.
47679          * @param {Form} this
47680          * @param {Action} action The action to be performed
47681          */
47682         beforeaction: true,
47683         /**
47684          * @event actionfailed
47685          * Fires when an action fails.
47686          * @param {Form} this
47687          * @param {Action} action The action that failed
47688          */
47689         actionfailed : true,
47690         /**
47691          * @event actioncomplete
47692          * Fires when an action is completed.
47693          * @param {Form} this
47694          * @param {Action} action The action that completed
47695          */
47696         actioncomplete : true
47697     });
47698     if(el){
47699         this.initEl(el);
47700     }
47701     Roo.form.BasicForm.superclass.constructor.call(this);
47702     
47703     Roo.form.BasicForm.popover.apply();
47704 };
47705
47706 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47707     /**
47708      * @cfg {String} method
47709      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47710      */
47711     /**
47712      * @cfg {DataReader} reader
47713      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47714      * This is optional as there is built-in support for processing JSON.
47715      */
47716     /**
47717      * @cfg {DataReader} errorReader
47718      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47719      * This is completely optional as there is built-in support for processing JSON.
47720      */
47721     /**
47722      * @cfg {String} url
47723      * The URL to use for form actions if one isn't supplied in the action options.
47724      */
47725     /**
47726      * @cfg {Boolean} fileUpload
47727      * Set to true if this form is a file upload.
47728      */
47729      
47730     /**
47731      * @cfg {Object} baseParams
47732      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47733      */
47734      /**
47735      
47736     /**
47737      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47738      */
47739     timeout: 30,
47740
47741     // private
47742     activeAction : null,
47743
47744     /**
47745      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47746      * or setValues() data instead of when the form was first created.
47747      */
47748     trackResetOnLoad : false,
47749     
47750     
47751     /**
47752      * childForms - used for multi-tab forms
47753      * @type {Array}
47754      */
47755     childForms : false,
47756     
47757     /**
47758      * allItems - full list of fields.
47759      * @type {Array}
47760      */
47761     allItems : false,
47762     
47763     /**
47764      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47765      * element by passing it or its id or mask the form itself by passing in true.
47766      * @type Mixed
47767      */
47768     waitMsgTarget : false,
47769     
47770     /**
47771      * @type Boolean
47772      */
47773     disableMask : false,
47774     
47775     /**
47776      * @cfg {Boolean} errorMask (true|false) default false
47777      */
47778     errorMask : false,
47779     
47780     /**
47781      * @cfg {Number} maskOffset Default 100
47782      */
47783     maskOffset : 100,
47784
47785     // private
47786     initEl : function(el){
47787         this.el = Roo.get(el);
47788         this.id = this.el.id || Roo.id();
47789         this.el.on('submit', this.onSubmit, this);
47790         this.el.addClass('x-form');
47791     },
47792
47793     // private
47794     onSubmit : function(e){
47795         e.stopEvent();
47796     },
47797
47798     /**
47799      * Returns true if client-side validation on the form is successful.
47800      * @return Boolean
47801      */
47802     isValid : function(){
47803         var valid = true;
47804         var target = false;
47805         this.items.each(function(f){
47806             if(f.validate()){
47807                 return;
47808             }
47809             
47810             valid = false;
47811                 
47812             if(!target && f.el.isVisible(true)){
47813                 target = f;
47814             }
47815         });
47816         
47817         if(this.errorMask && !valid){
47818             Roo.form.BasicForm.popover.mask(this, target);
47819         }
47820         
47821         return valid;
47822     },
47823     /**
47824      * Returns array of invalid form fields.
47825      * @return Array
47826      */
47827     
47828     invalidFields : function()
47829     {
47830         var ret = [];
47831         this.items.each(function(f){
47832             if(f.validate()){
47833                 return;
47834             }
47835             ret.push(f);
47836             
47837         });
47838         
47839         return ret;
47840     },
47841     
47842     
47843     /**
47844      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47845      * @return Boolean
47846      */
47847     isDirty : function(){
47848         var dirty = false;
47849         this.items.each(function(f){
47850            if(f.isDirty()){
47851                dirty = true;
47852                return false;
47853            }
47854         });
47855         return dirty;
47856     },
47857     
47858     /**
47859      * Returns true if any fields in this form have changed since their original load. (New version)
47860      * @return Boolean
47861      */
47862     
47863     hasChanged : function()
47864     {
47865         var dirty = false;
47866         this.items.each(function(f){
47867            if(f.hasChanged()){
47868                dirty = true;
47869                return false;
47870            }
47871         });
47872         return dirty;
47873         
47874     },
47875     /**
47876      * Resets all hasChanged to 'false' -
47877      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47878      * So hasChanged storage is only to be used for this purpose
47879      * @return Boolean
47880      */
47881     resetHasChanged : function()
47882     {
47883         this.items.each(function(f){
47884            f.resetHasChanged();
47885         });
47886         
47887     },
47888     
47889     
47890     /**
47891      * Performs a predefined action (submit or load) or custom actions you define on this form.
47892      * @param {String} actionName The name of the action type
47893      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47894      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47895      * accept other config options):
47896      * <pre>
47897 Property          Type             Description
47898 ----------------  ---------------  ----------------------------------------------------------------------------------
47899 url               String           The url for the action (defaults to the form's url)
47900 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47901 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47902 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47903                                    validate the form on the client (defaults to false)
47904      * </pre>
47905      * @return {BasicForm} this
47906      */
47907     doAction : function(action, options){
47908         if(typeof action == 'string'){
47909             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47910         }
47911         if(this.fireEvent('beforeaction', this, action) !== false){
47912             this.beforeAction(action);
47913             action.run.defer(100, action);
47914         }
47915         return this;
47916     },
47917
47918     /**
47919      * Shortcut to do a submit action.
47920      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47921      * @return {BasicForm} this
47922      */
47923     submit : function(options){
47924         this.doAction('submit', options);
47925         return this;
47926     },
47927
47928     /**
47929      * Shortcut to do a load action.
47930      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47931      * @return {BasicForm} this
47932      */
47933     load : function(options){
47934         this.doAction('load', options);
47935         return this;
47936     },
47937
47938     /**
47939      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47940      * @param {Record} record The record to edit
47941      * @return {BasicForm} this
47942      */
47943     updateRecord : function(record){
47944         record.beginEdit();
47945         var fs = record.fields;
47946         fs.each(function(f){
47947             var field = this.findField(f.name);
47948             if(field){
47949                 record.set(f.name, field.getValue());
47950             }
47951         }, this);
47952         record.endEdit();
47953         return this;
47954     },
47955
47956     /**
47957      * Loads an Roo.data.Record into this form.
47958      * @param {Record} record The record to load
47959      * @return {BasicForm} this
47960      */
47961     loadRecord : function(record){
47962         this.setValues(record.data);
47963         return this;
47964     },
47965
47966     // private
47967     beforeAction : function(action){
47968         var o = action.options;
47969         
47970         if(!this.disableMask) {
47971             if(this.waitMsgTarget === true){
47972                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47973             }else if(this.waitMsgTarget){
47974                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47975                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47976             }else {
47977                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47978             }
47979         }
47980         
47981          
47982     },
47983
47984     // private
47985     afterAction : function(action, success){
47986         this.activeAction = null;
47987         var o = action.options;
47988         
47989         if(!this.disableMask) {
47990             if(this.waitMsgTarget === true){
47991                 this.el.unmask();
47992             }else if(this.waitMsgTarget){
47993                 this.waitMsgTarget.unmask();
47994             }else{
47995                 Roo.MessageBox.updateProgress(1);
47996                 Roo.MessageBox.hide();
47997             }
47998         }
47999         
48000         if(success){
48001             if(o.reset){
48002                 this.reset();
48003             }
48004             Roo.callback(o.success, o.scope, [this, action]);
48005             this.fireEvent('actioncomplete', this, action);
48006             
48007         }else{
48008             
48009             // failure condition..
48010             // we have a scenario where updates need confirming.
48011             // eg. if a locking scenario exists..
48012             // we look for { errors : { needs_confirm : true }} in the response.
48013             if (
48014                 (typeof(action.result) != 'undefined')  &&
48015                 (typeof(action.result.errors) != 'undefined')  &&
48016                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48017            ){
48018                 var _t = this;
48019                 Roo.MessageBox.confirm(
48020                     "Change requires confirmation",
48021                     action.result.errorMsg,
48022                     function(r) {
48023                         if (r != 'yes') {
48024                             return;
48025                         }
48026                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48027                     }
48028                     
48029                 );
48030                 
48031                 
48032                 
48033                 return;
48034             }
48035             
48036             Roo.callback(o.failure, o.scope, [this, action]);
48037             // show an error message if no failed handler is set..
48038             if (!this.hasListener('actionfailed')) {
48039                 Roo.MessageBox.alert("Error",
48040                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48041                         action.result.errorMsg :
48042                         "Saving Failed, please check your entries or try again"
48043                 );
48044             }
48045             
48046             this.fireEvent('actionfailed', this, action);
48047         }
48048         
48049     },
48050
48051     /**
48052      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48053      * @param {String} id The value to search for
48054      * @return Field
48055      */
48056     findField : function(id){
48057         var field = this.items.get(id);
48058         if(!field){
48059             this.items.each(function(f){
48060                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48061                     field = f;
48062                     return false;
48063                 }
48064             });
48065         }
48066         return field || null;
48067     },
48068
48069     /**
48070      * Add a secondary form to this one, 
48071      * Used to provide tabbed forms. One form is primary, with hidden values 
48072      * which mirror the elements from the other forms.
48073      * 
48074      * @param {Roo.form.Form} form to add.
48075      * 
48076      */
48077     addForm : function(form)
48078     {
48079        
48080         if (this.childForms.indexOf(form) > -1) {
48081             // already added..
48082             return;
48083         }
48084         this.childForms.push(form);
48085         var n = '';
48086         Roo.each(form.allItems, function (fe) {
48087             
48088             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48089             if (this.findField(n)) { // already added..
48090                 return;
48091             }
48092             var add = new Roo.form.Hidden({
48093                 name : n
48094             });
48095             add.render(this.el);
48096             
48097             this.add( add );
48098         }, this);
48099         
48100     },
48101     /**
48102      * Mark fields in this form invalid in bulk.
48103      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48104      * @return {BasicForm} this
48105      */
48106     markInvalid : function(errors){
48107         if(errors instanceof Array){
48108             for(var i = 0, len = errors.length; i < len; i++){
48109                 var fieldError = errors[i];
48110                 var f = this.findField(fieldError.id);
48111                 if(f){
48112                     f.markInvalid(fieldError.msg);
48113                 }
48114             }
48115         }else{
48116             var field, id;
48117             for(id in errors){
48118                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48119                     field.markInvalid(errors[id]);
48120                 }
48121             }
48122         }
48123         Roo.each(this.childForms || [], function (f) {
48124             f.markInvalid(errors);
48125         });
48126         
48127         return this;
48128     },
48129
48130     /**
48131      * Set values for fields in this form in bulk.
48132      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48133      * @return {BasicForm} this
48134      */
48135     setValues : function(values){
48136         if(values instanceof Array){ // array of objects
48137             for(var i = 0, len = values.length; i < len; i++){
48138                 var v = values[i];
48139                 var f = this.findField(v.id);
48140                 if(f){
48141                     f.setValue(v.value);
48142                     if(this.trackResetOnLoad){
48143                         f.originalValue = f.getValue();
48144                     }
48145                 }
48146             }
48147         }else{ // object hash
48148             var field, id;
48149             for(id in values){
48150                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48151                     
48152                     if (field.setFromData && 
48153                         field.valueField && 
48154                         field.displayField &&
48155                         // combos' with local stores can 
48156                         // be queried via setValue()
48157                         // to set their value..
48158                         (field.store && !field.store.isLocal)
48159                         ) {
48160                         // it's a combo
48161                         var sd = { };
48162                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48163                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48164                         field.setFromData(sd);
48165                         
48166                     } else {
48167                         field.setValue(values[id]);
48168                     }
48169                     
48170                     
48171                     if(this.trackResetOnLoad){
48172                         field.originalValue = field.getValue();
48173                     }
48174                 }
48175             }
48176         }
48177         this.resetHasChanged();
48178         
48179         
48180         Roo.each(this.childForms || [], function (f) {
48181             f.setValues(values);
48182             f.resetHasChanged();
48183         });
48184                 
48185         return this;
48186     },
48187  
48188     /**
48189      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48190      * they are returned as an array.
48191      * @param {Boolean} asString
48192      * @return {Object}
48193      */
48194     getValues : function(asString){
48195         if (this.childForms) {
48196             // copy values from the child forms
48197             Roo.each(this.childForms, function (f) {
48198                 this.setValues(f.getValues());
48199             }, this);
48200         }
48201         
48202         // use formdata
48203         if (typeof(FormData) != 'undefined' && asString !== true) {
48204             // this relies on a 'recent' version of chrome apparently...
48205             try {
48206                 var fd = (new FormData(this.el.dom)).entries();
48207                 var ret = {};
48208                 var ent = fd.next();
48209                 while (!ent.done) {
48210                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48211                     ent = fd.next();
48212                 };
48213                 return ret;
48214             } catch(e) {
48215                 
48216             }
48217             
48218         }
48219         
48220         
48221         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48222         if(asString === true){
48223             return fs;
48224         }
48225         return Roo.urlDecode(fs);
48226     },
48227     
48228     /**
48229      * Returns the fields in this form as an object with key/value pairs. 
48230      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48231      * @return {Object}
48232      */
48233     getFieldValues : function(with_hidden)
48234     {
48235         if (this.childForms) {
48236             // copy values from the child forms
48237             // should this call getFieldValues - probably not as we do not currently copy
48238             // hidden fields when we generate..
48239             Roo.each(this.childForms, function (f) {
48240                 this.setValues(f.getValues());
48241             }, this);
48242         }
48243         
48244         var ret = {};
48245         this.items.each(function(f){
48246             if (!f.getName()) {
48247                 return;
48248             }
48249             var v = f.getValue();
48250             if (f.inputType =='radio') {
48251                 if (typeof(ret[f.getName()]) == 'undefined') {
48252                     ret[f.getName()] = ''; // empty..
48253                 }
48254                 
48255                 if (!f.el.dom.checked) {
48256                     return;
48257                     
48258                 }
48259                 v = f.el.dom.value;
48260                 
48261             }
48262             
48263             // not sure if this supported any more..
48264             if ((typeof(v) == 'object') && f.getRawValue) {
48265                 v = f.getRawValue() ; // dates..
48266             }
48267             // combo boxes where name != hiddenName...
48268             if (f.name != f.getName()) {
48269                 ret[f.name] = f.getRawValue();
48270             }
48271             ret[f.getName()] = v;
48272         });
48273         
48274         return ret;
48275     },
48276
48277     /**
48278      * Clears all invalid messages in this form.
48279      * @return {BasicForm} this
48280      */
48281     clearInvalid : function(){
48282         this.items.each(function(f){
48283            f.clearInvalid();
48284         });
48285         
48286         Roo.each(this.childForms || [], function (f) {
48287             f.clearInvalid();
48288         });
48289         
48290         
48291         return this;
48292     },
48293
48294     /**
48295      * Resets this form.
48296      * @return {BasicForm} this
48297      */
48298     reset : function(){
48299         this.items.each(function(f){
48300             f.reset();
48301         });
48302         
48303         Roo.each(this.childForms || [], function (f) {
48304             f.reset();
48305         });
48306         this.resetHasChanged();
48307         
48308         return this;
48309     },
48310
48311     /**
48312      * Add Roo.form components to this form.
48313      * @param {Field} field1
48314      * @param {Field} field2 (optional)
48315      * @param {Field} etc (optional)
48316      * @return {BasicForm} this
48317      */
48318     add : function(){
48319         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48320         return this;
48321     },
48322
48323
48324     /**
48325      * Removes a field from the items collection (does NOT remove its markup).
48326      * @param {Field} field
48327      * @return {BasicForm} this
48328      */
48329     remove : function(field){
48330         this.items.remove(field);
48331         return this;
48332     },
48333
48334     /**
48335      * Looks at the fields in this form, checks them for an id attribute,
48336      * and calls applyTo on the existing dom element with that id.
48337      * @return {BasicForm} this
48338      */
48339     render : function(){
48340         this.items.each(function(f){
48341             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48342                 f.applyTo(f.id);
48343             }
48344         });
48345         return this;
48346     },
48347
48348     /**
48349      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48350      * @param {Object} values
48351      * @return {BasicForm} this
48352      */
48353     applyToFields : function(o){
48354         this.items.each(function(f){
48355            Roo.apply(f, o);
48356         });
48357         return this;
48358     },
48359
48360     /**
48361      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48362      * @param {Object} values
48363      * @return {BasicForm} this
48364      */
48365     applyIfToFields : function(o){
48366         this.items.each(function(f){
48367            Roo.applyIf(f, o);
48368         });
48369         return this;
48370     }
48371 });
48372
48373 // back compat
48374 Roo.BasicForm = Roo.form.BasicForm;
48375
48376 Roo.apply(Roo.form.BasicForm, {
48377     
48378     popover : {
48379         
48380         padding : 5,
48381         
48382         isApplied : false,
48383         
48384         isMasked : false,
48385         
48386         form : false,
48387         
48388         target : false,
48389         
48390         intervalID : false,
48391         
48392         maskEl : false,
48393         
48394         apply : function()
48395         {
48396             if(this.isApplied){
48397                 return;
48398             }
48399             
48400             this.maskEl = {
48401                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48402                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48403                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48404                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48405             };
48406             
48407             this.maskEl.top.enableDisplayMode("block");
48408             this.maskEl.left.enableDisplayMode("block");
48409             this.maskEl.bottom.enableDisplayMode("block");
48410             this.maskEl.right.enableDisplayMode("block");
48411             
48412             Roo.get(document.body).on('click', function(){
48413                 this.unmask();
48414             }, this);
48415             
48416             Roo.get(document.body).on('touchstart', function(){
48417                 this.unmask();
48418             }, this);
48419             
48420             this.isApplied = true
48421         },
48422         
48423         mask : function(form, target)
48424         {
48425             this.form = form;
48426             
48427             this.target = target;
48428             
48429             if(!this.form.errorMask || !target.el){
48430                 return;
48431             }
48432             
48433             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48434             
48435             var ot = this.target.el.calcOffsetsTo(scrollable);
48436             
48437             var scrollTo = ot[1] - this.form.maskOffset;
48438             
48439             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48440             
48441             scrollable.scrollTo('top', scrollTo);
48442             
48443             var el = this.target.wrap || this.target.el;
48444             
48445             var box = el.getBox();
48446             
48447             this.maskEl.top.setStyle('position', 'absolute');
48448             this.maskEl.top.setStyle('z-index', 10000);
48449             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48450             this.maskEl.top.setLeft(0);
48451             this.maskEl.top.setTop(0);
48452             this.maskEl.top.show();
48453             
48454             this.maskEl.left.setStyle('position', 'absolute');
48455             this.maskEl.left.setStyle('z-index', 10000);
48456             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48457             this.maskEl.left.setLeft(0);
48458             this.maskEl.left.setTop(box.y - this.padding);
48459             this.maskEl.left.show();
48460
48461             this.maskEl.bottom.setStyle('position', 'absolute');
48462             this.maskEl.bottom.setStyle('z-index', 10000);
48463             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48464             this.maskEl.bottom.setLeft(0);
48465             this.maskEl.bottom.setTop(box.bottom + this.padding);
48466             this.maskEl.bottom.show();
48467
48468             this.maskEl.right.setStyle('position', 'absolute');
48469             this.maskEl.right.setStyle('z-index', 10000);
48470             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48471             this.maskEl.right.setLeft(box.right + this.padding);
48472             this.maskEl.right.setTop(box.y - this.padding);
48473             this.maskEl.right.show();
48474
48475             this.intervalID = window.setInterval(function() {
48476                 Roo.form.BasicForm.popover.unmask();
48477             }, 10000);
48478
48479             window.onwheel = function(){ return false;};
48480             
48481             (function(){ this.isMasked = true; }).defer(500, this);
48482             
48483         },
48484         
48485         unmask : function()
48486         {
48487             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48488                 return;
48489             }
48490             
48491             this.maskEl.top.setStyle('position', 'absolute');
48492             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48493             this.maskEl.top.hide();
48494
48495             this.maskEl.left.setStyle('position', 'absolute');
48496             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48497             this.maskEl.left.hide();
48498
48499             this.maskEl.bottom.setStyle('position', 'absolute');
48500             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48501             this.maskEl.bottom.hide();
48502
48503             this.maskEl.right.setStyle('position', 'absolute');
48504             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48505             this.maskEl.right.hide();
48506             
48507             window.onwheel = function(){ return true;};
48508             
48509             if(this.intervalID){
48510                 window.clearInterval(this.intervalID);
48511                 this.intervalID = false;
48512             }
48513             
48514             this.isMasked = false;
48515             
48516         }
48517         
48518     }
48519     
48520 });/*
48521  * Based on:
48522  * Ext JS Library 1.1.1
48523  * Copyright(c) 2006-2007, Ext JS, LLC.
48524  *
48525  * Originally Released Under LGPL - original licence link has changed is not relivant.
48526  *
48527  * Fork - LGPL
48528  * <script type="text/javascript">
48529  */
48530
48531 /**
48532  * @class Roo.form.Form
48533  * @extends Roo.form.BasicForm
48534  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
48535  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48536  * @constructor
48537  * @param {Object} config Configuration options
48538  */
48539 Roo.form.Form = function(config){
48540     var xitems =  [];
48541     if (config.items) {
48542         xitems = config.items;
48543         delete config.items;
48544     }
48545    
48546     
48547     Roo.form.Form.superclass.constructor.call(this, null, config);
48548     this.url = this.url || this.action;
48549     if(!this.root){
48550         this.root = new Roo.form.Layout(Roo.applyIf({
48551             id: Roo.id()
48552         }, config));
48553     }
48554     this.active = this.root;
48555     /**
48556      * Array of all the buttons that have been added to this form via {@link addButton}
48557      * @type Array
48558      */
48559     this.buttons = [];
48560     this.allItems = [];
48561     this.addEvents({
48562         /**
48563          * @event clientvalidation
48564          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48565          * @param {Form} this
48566          * @param {Boolean} valid true if the form has passed client-side validation
48567          */
48568         clientvalidation: true,
48569         /**
48570          * @event rendered
48571          * Fires when the form is rendered
48572          * @param {Roo.form.Form} form
48573          */
48574         rendered : true
48575     });
48576     
48577     if (this.progressUrl) {
48578             // push a hidden field onto the list of fields..
48579             this.addxtype( {
48580                     xns: Roo.form, 
48581                     xtype : 'Hidden', 
48582                     name : 'UPLOAD_IDENTIFIER' 
48583             });
48584         }
48585         
48586     
48587     Roo.each(xitems, this.addxtype, this);
48588     
48589 };
48590
48591 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48592      /**
48593      * @cfg {Roo.Button} buttons[] buttons at bottom of form
48594      */
48595     
48596     /**
48597      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48598      */
48599     /**
48600      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48601      */
48602     /**
48603      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48604      */
48605     buttonAlign:'center',
48606
48607     /**
48608      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48609      */
48610     minButtonWidth:75,
48611
48612     /**
48613      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48614      * This property cascades to child containers if not set.
48615      */
48616     labelAlign:'left',
48617
48618     /**
48619      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48620      * fires a looping event with that state. This is required to bind buttons to the valid
48621      * state using the config value formBind:true on the button.
48622      */
48623     monitorValid : false,
48624
48625     /**
48626      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48627      */
48628     monitorPoll : 200,
48629     
48630     /**
48631      * @cfg {String} progressUrl - Url to return progress data 
48632      */
48633     
48634     progressUrl : false,
48635     /**
48636      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48637      * sending a formdata with extra parameters - eg uploaded elements.
48638      */
48639     
48640     formData : false,
48641     
48642     /**
48643      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48644      * fields are added and the column is closed. If no fields are passed the column remains open
48645      * until end() is called.
48646      * @param {Object} config The config to pass to the column
48647      * @param {Field} field1 (optional)
48648      * @param {Field} field2 (optional)
48649      * @param {Field} etc (optional)
48650      * @return Column The column container object
48651      */
48652     column : function(c){
48653         var col = new Roo.form.Column(c);
48654         this.start(col);
48655         if(arguments.length > 1){ // duplicate code required because of Opera
48656             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48657             this.end();
48658         }
48659         return col;
48660     },
48661
48662     /**
48663      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48664      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48665      * until end() is called.
48666      * @param {Object} config The config to pass to the fieldset
48667      * @param {Field} field1 (optional)
48668      * @param {Field} field2 (optional)
48669      * @param {Field} etc (optional)
48670      * @return FieldSet The fieldset container object
48671      */
48672     fieldset : function(c){
48673         var fs = new Roo.form.FieldSet(c);
48674         this.start(fs);
48675         if(arguments.length > 1){ // duplicate code required because of Opera
48676             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48677             this.end();
48678         }
48679         return fs;
48680     },
48681
48682     /**
48683      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48684      * fields are added and the container is closed. If no fields are passed the container remains open
48685      * until end() is called.
48686      * @param {Object} config The config to pass to the Layout
48687      * @param {Field} field1 (optional)
48688      * @param {Field} field2 (optional)
48689      * @param {Field} etc (optional)
48690      * @return Layout The container object
48691      */
48692     container : function(c){
48693         var l = new Roo.form.Layout(c);
48694         this.start(l);
48695         if(arguments.length > 1){ // duplicate code required because of Opera
48696             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48697             this.end();
48698         }
48699         return l;
48700     },
48701
48702     /**
48703      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48704      * @param {Object} container A Roo.form.Layout or subclass of Layout
48705      * @return {Form} this
48706      */
48707     start : function(c){
48708         // cascade label info
48709         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48710         this.active.stack.push(c);
48711         c.ownerCt = this.active;
48712         this.active = c;
48713         return this;
48714     },
48715
48716     /**
48717      * Closes the current open container
48718      * @return {Form} this
48719      */
48720     end : function(){
48721         if(this.active == this.root){
48722             return this;
48723         }
48724         this.active = this.active.ownerCt;
48725         return this;
48726     },
48727
48728     /**
48729      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48730      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48731      * as the label of the field.
48732      * @param {Field} field1
48733      * @param {Field} field2 (optional)
48734      * @param {Field} etc. (optional)
48735      * @return {Form} this
48736      */
48737     add : function(){
48738         this.active.stack.push.apply(this.active.stack, arguments);
48739         this.allItems.push.apply(this.allItems,arguments);
48740         var r = [];
48741         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48742             if(a[i].isFormField){
48743                 r.push(a[i]);
48744             }
48745         }
48746         if(r.length > 0){
48747             Roo.form.Form.superclass.add.apply(this, r);
48748         }
48749         return this;
48750     },
48751     
48752
48753     
48754     
48755     
48756      /**
48757      * Find any element that has been added to a form, using it's ID or name
48758      * This can include framesets, columns etc. along with regular fields..
48759      * @param {String} id - id or name to find.
48760      
48761      * @return {Element} e - or false if nothing found.
48762      */
48763     findbyId : function(id)
48764     {
48765         var ret = false;
48766         if (!id) {
48767             return ret;
48768         }
48769         Roo.each(this.allItems, function(f){
48770             if (f.id == id || f.name == id ){
48771                 ret = f;
48772                 return false;
48773             }
48774         });
48775         return ret;
48776     },
48777
48778     
48779     
48780     /**
48781      * Render this form into the passed container. This should only be called once!
48782      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48783      * @return {Form} this
48784      */
48785     render : function(ct)
48786     {
48787         
48788         
48789         
48790         ct = Roo.get(ct);
48791         var o = this.autoCreate || {
48792             tag: 'form',
48793             method : this.method || 'POST',
48794             id : this.id || Roo.id()
48795         };
48796         this.initEl(ct.createChild(o));
48797
48798         this.root.render(this.el);
48799         
48800        
48801              
48802         this.items.each(function(f){
48803             f.render('x-form-el-'+f.id);
48804         });
48805
48806         if(this.buttons.length > 0){
48807             // tables are required to maintain order and for correct IE layout
48808             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48809                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48810                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48811             }}, null, true);
48812             var tr = tb.getElementsByTagName('tr')[0];
48813             for(var i = 0, len = this.buttons.length; i < len; i++) {
48814                 var b = this.buttons[i];
48815                 var td = document.createElement('td');
48816                 td.className = 'x-form-btn-td';
48817                 b.render(tr.appendChild(td));
48818             }
48819         }
48820         if(this.monitorValid){ // initialize after render
48821             this.startMonitoring();
48822         }
48823         this.fireEvent('rendered', this);
48824         return this;
48825     },
48826
48827     /**
48828      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48829      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48830      * object or a valid Roo.DomHelper element config
48831      * @param {Function} handler The function called when the button is clicked
48832      * @param {Object} scope (optional) The scope of the handler function
48833      * @return {Roo.Button}
48834      */
48835     addButton : function(config, handler, scope){
48836         var bc = {
48837             handler: handler,
48838             scope: scope,
48839             minWidth: this.minButtonWidth,
48840             hideParent:true
48841         };
48842         if(typeof config == "string"){
48843             bc.text = config;
48844         }else{
48845             Roo.apply(bc, config);
48846         }
48847         var btn = new Roo.Button(null, bc);
48848         this.buttons.push(btn);
48849         return btn;
48850     },
48851
48852      /**
48853      * Adds a series of form elements (using the xtype property as the factory method.
48854      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48855      * @param {Object} config 
48856      */
48857     
48858     addxtype : function()
48859     {
48860         var ar = Array.prototype.slice.call(arguments, 0);
48861         var ret = false;
48862         for(var i = 0; i < ar.length; i++) {
48863             if (!ar[i]) {
48864                 continue; // skip -- if this happends something invalid got sent, we 
48865                 // should ignore it, as basically that interface element will not show up
48866                 // and that should be pretty obvious!!
48867             }
48868             
48869             if (Roo.form[ar[i].xtype]) {
48870                 ar[i].form = this;
48871                 var fe = Roo.factory(ar[i], Roo.form);
48872                 if (!ret) {
48873                     ret = fe;
48874                 }
48875                 fe.form = this;
48876                 if (fe.store) {
48877                     fe.store.form = this;
48878                 }
48879                 if (fe.isLayout) {  
48880                          
48881                     this.start(fe);
48882                     this.allItems.push(fe);
48883                     if (fe.items && fe.addxtype) {
48884                         fe.addxtype.apply(fe, fe.items);
48885                         delete fe.items;
48886                     }
48887                      this.end();
48888                     continue;
48889                 }
48890                 
48891                 
48892                  
48893                 this.add(fe);
48894               //  console.log('adding ' + ar[i].xtype);
48895             }
48896             if (ar[i].xtype == 'Button') {  
48897                 //console.log('adding button');
48898                 //console.log(ar[i]);
48899                 this.addButton(ar[i]);
48900                 this.allItems.push(fe);
48901                 continue;
48902             }
48903             
48904             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48905                 alert('end is not supported on xtype any more, use items');
48906             //    this.end();
48907             //    //console.log('adding end');
48908             }
48909             
48910         }
48911         return ret;
48912     },
48913     
48914     /**
48915      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48916      * option "monitorValid"
48917      */
48918     startMonitoring : function(){
48919         if(!this.bound){
48920             this.bound = true;
48921             Roo.TaskMgr.start({
48922                 run : this.bindHandler,
48923                 interval : this.monitorPoll || 200,
48924                 scope: this
48925             });
48926         }
48927     },
48928
48929     /**
48930      * Stops monitoring of the valid state of this form
48931      */
48932     stopMonitoring : function(){
48933         this.bound = false;
48934     },
48935
48936     // private
48937     bindHandler : function(){
48938         if(!this.bound){
48939             return false; // stops binding
48940         }
48941         var valid = true;
48942         this.items.each(function(f){
48943             if(!f.isValid(true)){
48944                 valid = false;
48945                 return false;
48946             }
48947         });
48948         for(var i = 0, len = this.buttons.length; i < len; i++){
48949             var btn = this.buttons[i];
48950             if(btn.formBind === true && btn.disabled === valid){
48951                 btn.setDisabled(!valid);
48952             }
48953         }
48954         this.fireEvent('clientvalidation', this, valid);
48955     }
48956     
48957     
48958     
48959     
48960     
48961     
48962     
48963     
48964 });
48965
48966
48967 // back compat
48968 Roo.Form = Roo.form.Form;
48969 /*
48970  * Based on:
48971  * Ext JS Library 1.1.1
48972  * Copyright(c) 2006-2007, Ext JS, LLC.
48973  *
48974  * Originally Released Under LGPL - original licence link has changed is not relivant.
48975  *
48976  * Fork - LGPL
48977  * <script type="text/javascript">
48978  */
48979
48980 // as we use this in bootstrap.
48981 Roo.namespace('Roo.form');
48982  /**
48983  * @class Roo.form.Action
48984  * Internal Class used to handle form actions
48985  * @constructor
48986  * @param {Roo.form.BasicForm} el The form element or its id
48987  * @param {Object} config Configuration options
48988  */
48989
48990  
48991  
48992 // define the action interface
48993 Roo.form.Action = function(form, options){
48994     this.form = form;
48995     this.options = options || {};
48996 };
48997 /**
48998  * Client Validation Failed
48999  * @const 
49000  */
49001 Roo.form.Action.CLIENT_INVALID = 'client';
49002 /**
49003  * Server Validation Failed
49004  * @const 
49005  */
49006 Roo.form.Action.SERVER_INVALID = 'server';
49007  /**
49008  * Connect to Server Failed
49009  * @const 
49010  */
49011 Roo.form.Action.CONNECT_FAILURE = 'connect';
49012 /**
49013  * Reading Data from Server Failed
49014  * @const 
49015  */
49016 Roo.form.Action.LOAD_FAILURE = 'load';
49017
49018 Roo.form.Action.prototype = {
49019     type : 'default',
49020     failureType : undefined,
49021     response : undefined,
49022     result : undefined,
49023
49024     // interface method
49025     run : function(options){
49026
49027     },
49028
49029     // interface method
49030     success : function(response){
49031
49032     },
49033
49034     // interface method
49035     handleResponse : function(response){
49036
49037     },
49038
49039     // default connection failure
49040     failure : function(response){
49041         
49042         this.response = response;
49043         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49044         this.form.afterAction(this, false);
49045     },
49046
49047     processResponse : function(response){
49048         this.response = response;
49049         if(!response.responseText){
49050             return true;
49051         }
49052         this.result = this.handleResponse(response);
49053         return this.result;
49054     },
49055
49056     // utility functions used internally
49057     getUrl : function(appendParams){
49058         var url = this.options.url || this.form.url || this.form.el.dom.action;
49059         if(appendParams){
49060             var p = this.getParams();
49061             if(p){
49062                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49063             }
49064         }
49065         return url;
49066     },
49067
49068     getMethod : function(){
49069         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49070     },
49071
49072     getParams : function(){
49073         var bp = this.form.baseParams;
49074         var p = this.options.params;
49075         if(p){
49076             if(typeof p == "object"){
49077                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49078             }else if(typeof p == 'string' && bp){
49079                 p += '&' + Roo.urlEncode(bp);
49080             }
49081         }else if(bp){
49082             p = Roo.urlEncode(bp);
49083         }
49084         return p;
49085     },
49086
49087     createCallback : function(){
49088         return {
49089             success: this.success,
49090             failure: this.failure,
49091             scope: this,
49092             timeout: (this.form.timeout*1000),
49093             upload: this.form.fileUpload ? this.success : undefined
49094         };
49095     }
49096 };
49097
49098 Roo.form.Action.Submit = function(form, options){
49099     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49100 };
49101
49102 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49103     type : 'submit',
49104
49105     haveProgress : false,
49106     uploadComplete : false,
49107     
49108     // uploadProgress indicator.
49109     uploadProgress : function()
49110     {
49111         if (!this.form.progressUrl) {
49112             return;
49113         }
49114         
49115         if (!this.haveProgress) {
49116             Roo.MessageBox.progress("Uploading", "Uploading");
49117         }
49118         if (this.uploadComplete) {
49119            Roo.MessageBox.hide();
49120            return;
49121         }
49122         
49123         this.haveProgress = true;
49124    
49125         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49126         
49127         var c = new Roo.data.Connection();
49128         c.request({
49129             url : this.form.progressUrl,
49130             params: {
49131                 id : uid
49132             },
49133             method: 'GET',
49134             success : function(req){
49135                //console.log(data);
49136                 var rdata = false;
49137                 var edata;
49138                 try  {
49139                    rdata = Roo.decode(req.responseText)
49140                 } catch (e) {
49141                     Roo.log("Invalid data from server..");
49142                     Roo.log(edata);
49143                     return;
49144                 }
49145                 if (!rdata || !rdata.success) {
49146                     Roo.log(rdata);
49147                     Roo.MessageBox.alert(Roo.encode(rdata));
49148                     return;
49149                 }
49150                 var data = rdata.data;
49151                 
49152                 if (this.uploadComplete) {
49153                    Roo.MessageBox.hide();
49154                    return;
49155                 }
49156                    
49157                 if (data){
49158                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49159                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49160                     );
49161                 }
49162                 this.uploadProgress.defer(2000,this);
49163             },
49164        
49165             failure: function(data) {
49166                 Roo.log('progress url failed ');
49167                 Roo.log(data);
49168             },
49169             scope : this
49170         });
49171            
49172     },
49173     
49174     
49175     run : function()
49176     {
49177         // run get Values on the form, so it syncs any secondary forms.
49178         this.form.getValues();
49179         
49180         var o = this.options;
49181         var method = this.getMethod();
49182         var isPost = method == 'POST';
49183         if(o.clientValidation === false || this.form.isValid()){
49184             
49185             if (this.form.progressUrl) {
49186                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49187                     (new Date() * 1) + '' + Math.random());
49188                     
49189             } 
49190             
49191             
49192             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49193                 form:this.form.el.dom,
49194                 url:this.getUrl(!isPost),
49195                 method: method,
49196                 params:isPost ? this.getParams() : null,
49197                 isUpload: this.form.fileUpload,
49198                 formData : this.form.formData
49199             }));
49200             
49201             this.uploadProgress();
49202
49203         }else if (o.clientValidation !== false){ // client validation failed
49204             this.failureType = Roo.form.Action.CLIENT_INVALID;
49205             this.form.afterAction(this, false);
49206         }
49207     },
49208
49209     success : function(response)
49210     {
49211         this.uploadComplete= true;
49212         if (this.haveProgress) {
49213             Roo.MessageBox.hide();
49214         }
49215         
49216         
49217         var result = this.processResponse(response);
49218         if(result === true || result.success){
49219             this.form.afterAction(this, true);
49220             return;
49221         }
49222         if(result.errors){
49223             this.form.markInvalid(result.errors);
49224             this.failureType = Roo.form.Action.SERVER_INVALID;
49225         }
49226         this.form.afterAction(this, false);
49227     },
49228     failure : function(response)
49229     {
49230         this.uploadComplete= true;
49231         if (this.haveProgress) {
49232             Roo.MessageBox.hide();
49233         }
49234         
49235         this.response = response;
49236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49237         this.form.afterAction(this, false);
49238     },
49239     
49240     handleResponse : function(response){
49241         if(this.form.errorReader){
49242             var rs = this.form.errorReader.read(response);
49243             var errors = [];
49244             if(rs.records){
49245                 for(var i = 0, len = rs.records.length; i < len; i++) {
49246                     var r = rs.records[i];
49247                     errors[i] = r.data;
49248                 }
49249             }
49250             if(errors.length < 1){
49251                 errors = null;
49252             }
49253             return {
49254                 success : rs.success,
49255                 errors : errors
49256             };
49257         }
49258         var ret = false;
49259         try {
49260             ret = Roo.decode(response.responseText);
49261         } catch (e) {
49262             ret = {
49263                 success: false,
49264                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49265                 errors : []
49266             };
49267         }
49268         return ret;
49269         
49270     }
49271 });
49272
49273
49274 Roo.form.Action.Load = function(form, options){
49275     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49276     this.reader = this.form.reader;
49277 };
49278
49279 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49280     type : 'load',
49281
49282     run : function(){
49283         
49284         Roo.Ajax.request(Roo.apply(
49285                 this.createCallback(), {
49286                     method:this.getMethod(),
49287                     url:this.getUrl(false),
49288                     params:this.getParams()
49289         }));
49290     },
49291
49292     success : function(response){
49293         
49294         var result = this.processResponse(response);
49295         if(result === true || !result.success || !result.data){
49296             this.failureType = Roo.form.Action.LOAD_FAILURE;
49297             this.form.afterAction(this, false);
49298             return;
49299         }
49300         this.form.clearInvalid();
49301         this.form.setValues(result.data);
49302         this.form.afterAction(this, true);
49303     },
49304
49305     handleResponse : function(response){
49306         if(this.form.reader){
49307             var rs = this.form.reader.read(response);
49308             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49309             return {
49310                 success : rs.success,
49311                 data : data
49312             };
49313         }
49314         return Roo.decode(response.responseText);
49315     }
49316 });
49317
49318 Roo.form.Action.ACTION_TYPES = {
49319     'load' : Roo.form.Action.Load,
49320     'submit' : Roo.form.Action.Submit
49321 };/*
49322  * Based on:
49323  * Ext JS Library 1.1.1
49324  * Copyright(c) 2006-2007, Ext JS, LLC.
49325  *
49326  * Originally Released Under LGPL - original licence link has changed is not relivant.
49327  *
49328  * Fork - LGPL
49329  * <script type="text/javascript">
49330  */
49331  
49332 /**
49333  * @class Roo.form.Layout
49334  * @extends Roo.Component
49335  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49336  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49337  * @constructor
49338  * @param {Object} config Configuration options
49339  */
49340 Roo.form.Layout = function(config){
49341     var xitems = [];
49342     if (config.items) {
49343         xitems = config.items;
49344         delete config.items;
49345     }
49346     Roo.form.Layout.superclass.constructor.call(this, config);
49347     this.stack = [];
49348     Roo.each(xitems, this.addxtype, this);
49349      
49350 };
49351
49352 Roo.extend(Roo.form.Layout, Roo.Component, {
49353     /**
49354      * @cfg {String/Object} autoCreate
49355      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49356      */
49357     /**
49358      * @cfg {String/Object/Function} style
49359      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49360      * a function which returns such a specification.
49361      */
49362     /**
49363      * @cfg {String} labelAlign
49364      * Valid values are "left," "top" and "right" (defaults to "left")
49365      */
49366     /**
49367      * @cfg {Number} labelWidth
49368      * Fixed width in pixels of all field labels (defaults to undefined)
49369      */
49370     /**
49371      * @cfg {Boolean} clear
49372      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49373      */
49374     clear : true,
49375     /**
49376      * @cfg {String} labelSeparator
49377      * The separator to use after field labels (defaults to ':')
49378      */
49379     labelSeparator : ':',
49380     /**
49381      * @cfg {Boolean} hideLabels
49382      * True to suppress the display of field labels in this layout (defaults to false)
49383      */
49384     hideLabels : false,
49385
49386     // private
49387     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49388     
49389     isLayout : true,
49390     
49391     // private
49392     onRender : function(ct, position){
49393         if(this.el){ // from markup
49394             this.el = Roo.get(this.el);
49395         }else {  // generate
49396             var cfg = this.getAutoCreate();
49397             this.el = ct.createChild(cfg, position);
49398         }
49399         if(this.style){
49400             this.el.applyStyles(this.style);
49401         }
49402         if(this.labelAlign){
49403             this.el.addClass('x-form-label-'+this.labelAlign);
49404         }
49405         if(this.hideLabels){
49406             this.labelStyle = "display:none";
49407             this.elementStyle = "padding-left:0;";
49408         }else{
49409             if(typeof this.labelWidth == 'number'){
49410                 this.labelStyle = "width:"+this.labelWidth+"px;";
49411                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49412             }
49413             if(this.labelAlign == 'top'){
49414                 this.labelStyle = "width:auto;";
49415                 this.elementStyle = "padding-left:0;";
49416             }
49417         }
49418         var stack = this.stack;
49419         var slen = stack.length;
49420         if(slen > 0){
49421             if(!this.fieldTpl){
49422                 var t = new Roo.Template(
49423                     '<div class="x-form-item {5}">',
49424                         '<label for="{0}" style="{2}">{1}{4}</label>',
49425                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49426                         '</div>',
49427                     '</div><div class="x-form-clear-left"></div>'
49428                 );
49429                 t.disableFormats = true;
49430                 t.compile();
49431                 Roo.form.Layout.prototype.fieldTpl = t;
49432             }
49433             for(var i = 0; i < slen; i++) {
49434                 if(stack[i].isFormField){
49435                     this.renderField(stack[i]);
49436                 }else{
49437                     this.renderComponent(stack[i]);
49438                 }
49439             }
49440         }
49441         if(this.clear){
49442             this.el.createChild({cls:'x-form-clear'});
49443         }
49444     },
49445
49446     // private
49447     renderField : function(f){
49448         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49449                f.id, //0
49450                f.fieldLabel, //1
49451                f.labelStyle||this.labelStyle||'', //2
49452                this.elementStyle||'', //3
49453                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49454                f.itemCls||this.itemCls||''  //5
49455        ], true).getPrevSibling());
49456     },
49457
49458     // private
49459     renderComponent : function(c){
49460         c.render(c.isLayout ? this.el : this.el.createChild());    
49461     },
49462     /**
49463      * Adds a object form elements (using the xtype property as the factory method.)
49464      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49465      * @param {Object} config 
49466      */
49467     addxtype : function(o)
49468     {
49469         // create the lement.
49470         o.form = this.form;
49471         var fe = Roo.factory(o, Roo.form);
49472         this.form.allItems.push(fe);
49473         this.stack.push(fe);
49474         
49475         if (fe.isFormField) {
49476             this.form.items.add(fe);
49477         }
49478          
49479         return fe;
49480     }
49481 });
49482
49483 /**
49484  * @class Roo.form.Column
49485  * @extends Roo.form.Layout
49486  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49487  * @constructor
49488  * @param {Object} config Configuration options
49489  */
49490 Roo.form.Column = function(config){
49491     Roo.form.Column.superclass.constructor.call(this, config);
49492 };
49493
49494 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49495     /**
49496      * @cfg {Number/String} width
49497      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49498      */
49499     /**
49500      * @cfg {String/Object} autoCreate
49501      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49502      */
49503
49504     // private
49505     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49506
49507     // private
49508     onRender : function(ct, position){
49509         Roo.form.Column.superclass.onRender.call(this, ct, position);
49510         if(this.width){
49511             this.el.setWidth(this.width);
49512         }
49513     }
49514 });
49515
49516
49517 /**
49518  * @class Roo.form.Row
49519  * @extends Roo.form.Layout
49520  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49521  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49522  * @constructor
49523  * @param {Object} config Configuration options
49524  */
49525
49526  
49527 Roo.form.Row = function(config){
49528     Roo.form.Row.superclass.constructor.call(this, config);
49529 };
49530  
49531 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49532       /**
49533      * @cfg {Number/String} width
49534      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49535      */
49536     /**
49537      * @cfg {Number/String} height
49538      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49539      */
49540     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49541     
49542     padWidth : 20,
49543     // private
49544     onRender : function(ct, position){
49545         //console.log('row render');
49546         if(!this.rowTpl){
49547             var t = new Roo.Template(
49548                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49549                     '<label for="{0}" style="{2}">{1}{4}</label>',
49550                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49551                     '</div>',
49552                 '</div>'
49553             );
49554             t.disableFormats = true;
49555             t.compile();
49556             Roo.form.Layout.prototype.rowTpl = t;
49557         }
49558         this.fieldTpl = this.rowTpl;
49559         
49560         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49561         var labelWidth = 100;
49562         
49563         if ((this.labelAlign != 'top')) {
49564             if (typeof this.labelWidth == 'number') {
49565                 labelWidth = this.labelWidth
49566             }
49567             this.padWidth =  20 + labelWidth;
49568             
49569         }
49570         
49571         Roo.form.Column.superclass.onRender.call(this, ct, position);
49572         if(this.width){
49573             this.el.setWidth(this.width);
49574         }
49575         if(this.height){
49576             this.el.setHeight(this.height);
49577         }
49578     },
49579     
49580     // private
49581     renderField : function(f){
49582         f.fieldEl = this.fieldTpl.append(this.el, [
49583                f.id, f.fieldLabel,
49584                f.labelStyle||this.labelStyle||'',
49585                this.elementStyle||'',
49586                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49587                f.itemCls||this.itemCls||'',
49588                f.width ? f.width + this.padWidth : 160 + this.padWidth
49589        ],true);
49590     }
49591 });
49592  
49593
49594 /**
49595  * @class Roo.form.FieldSet
49596  * @extends Roo.form.Layout
49597  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49598  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49599  * @constructor
49600  * @param {Object} config Configuration options
49601  */
49602 Roo.form.FieldSet = function(config){
49603     Roo.form.FieldSet.superclass.constructor.call(this, config);
49604 };
49605
49606 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49607     /**
49608      * @cfg {String} legend
49609      * The text to display as the legend for the FieldSet (defaults to '')
49610      */
49611     /**
49612      * @cfg {String/Object} autoCreate
49613      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49614      */
49615
49616     // private
49617     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49618
49619     // private
49620     onRender : function(ct, position){
49621         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49622         if(this.legend){
49623             this.setLegend(this.legend);
49624         }
49625     },
49626
49627     // private
49628     setLegend : function(text){
49629         if(this.rendered){
49630             this.el.child('legend').update(text);
49631         }
49632     }
49633 });/*
49634  * Based on:
49635  * Ext JS Library 1.1.1
49636  * Copyright(c) 2006-2007, Ext JS, LLC.
49637  *
49638  * Originally Released Under LGPL - original licence link has changed is not relivant.
49639  *
49640  * Fork - LGPL
49641  * <script type="text/javascript">
49642  */
49643 /**
49644  * @class Roo.form.VTypes
49645  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49646  * @static
49647  */
49648 Roo.form.VTypes = function(){
49649     // closure these in so they are only created once.
49650     var alpha = /^[a-zA-Z_]+$/;
49651     var alphanum = /^[a-zA-Z0-9_]+$/;
49652     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49653     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49654
49655     // All these messages and functions are configurable
49656     return {
49657         /**
49658          * The function used to validate email addresses
49659          * @param {String} value The email address
49660          */
49661         'email' : function(v){
49662             return email.test(v);
49663         },
49664         /**
49665          * The error text to display when the email validation function returns false
49666          * @type String
49667          */
49668         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49669         /**
49670          * The keystroke filter mask to be applied on email input
49671          * @type RegExp
49672          */
49673         'emailMask' : /[a-z0-9_\.\-@]/i,
49674
49675         /**
49676          * The function used to validate URLs
49677          * @param {String} value The URL
49678          */
49679         'url' : function(v){
49680             return url.test(v);
49681         },
49682         /**
49683          * The error text to display when the url validation function returns false
49684          * @type String
49685          */
49686         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49687         
49688         /**
49689          * The function used to validate alpha values
49690          * @param {String} value The value
49691          */
49692         'alpha' : function(v){
49693             return alpha.test(v);
49694         },
49695         /**
49696          * The error text to display when the alpha validation function returns false
49697          * @type String
49698          */
49699         'alphaText' : 'This field should only contain letters and _',
49700         /**
49701          * The keystroke filter mask to be applied on alpha input
49702          * @type RegExp
49703          */
49704         'alphaMask' : /[a-z_]/i,
49705
49706         /**
49707          * The function used to validate alphanumeric values
49708          * @param {String} value The value
49709          */
49710         'alphanum' : function(v){
49711             return alphanum.test(v);
49712         },
49713         /**
49714          * The error text to display when the alphanumeric validation function returns false
49715          * @type String
49716          */
49717         'alphanumText' : 'This field should only contain letters, numbers and _',
49718         /**
49719          * The keystroke filter mask to be applied on alphanumeric input
49720          * @type RegExp
49721          */
49722         'alphanumMask' : /[a-z0-9_]/i
49723     };
49724 }();//<script type="text/javascript">
49725
49726 /**
49727  * @class Roo.form.FCKeditor
49728  * @extends Roo.form.TextArea
49729  * Wrapper around the FCKEditor http://www.fckeditor.net
49730  * @constructor
49731  * Creates a new FCKeditor
49732  * @param {Object} config Configuration options
49733  */
49734 Roo.form.FCKeditor = function(config){
49735     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49736     this.addEvents({
49737          /**
49738          * @event editorinit
49739          * Fired when the editor is initialized - you can add extra handlers here..
49740          * @param {FCKeditor} this
49741          * @param {Object} the FCK object.
49742          */
49743         editorinit : true
49744     });
49745     
49746     
49747 };
49748 Roo.form.FCKeditor.editors = { };
49749 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49750 {
49751     //defaultAutoCreate : {
49752     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49753     //},
49754     // private
49755     /**
49756      * @cfg {Object} fck options - see fck manual for details.
49757      */
49758     fckconfig : false,
49759     
49760     /**
49761      * @cfg {Object} fck toolbar set (Basic or Default)
49762      */
49763     toolbarSet : 'Basic',
49764     /**
49765      * @cfg {Object} fck BasePath
49766      */ 
49767     basePath : '/fckeditor/',
49768     
49769     
49770     frame : false,
49771     
49772     value : '',
49773     
49774    
49775     onRender : function(ct, position)
49776     {
49777         if(!this.el){
49778             this.defaultAutoCreate = {
49779                 tag: "textarea",
49780                 style:"width:300px;height:60px;",
49781                 autocomplete: "new-password"
49782             };
49783         }
49784         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49785         /*
49786         if(this.grow){
49787             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49788             if(this.preventScrollbars){
49789                 this.el.setStyle("overflow", "hidden");
49790             }
49791             this.el.setHeight(this.growMin);
49792         }
49793         */
49794         //console.log('onrender' + this.getId() );
49795         Roo.form.FCKeditor.editors[this.getId()] = this;
49796          
49797
49798         this.replaceTextarea() ;
49799         
49800     },
49801     
49802     getEditor : function() {
49803         return this.fckEditor;
49804     },
49805     /**
49806      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49807      * @param {Mixed} value The value to set
49808      */
49809     
49810     
49811     setValue : function(value)
49812     {
49813         //console.log('setValue: ' + value);
49814         
49815         if(typeof(value) == 'undefined') { // not sure why this is happending...
49816             return;
49817         }
49818         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49819         
49820         //if(!this.el || !this.getEditor()) {
49821         //    this.value = value;
49822             //this.setValue.defer(100,this,[value]);    
49823         //    return;
49824         //} 
49825         
49826         if(!this.getEditor()) {
49827             return;
49828         }
49829         
49830         this.getEditor().SetData(value);
49831         
49832         //
49833
49834     },
49835
49836     /**
49837      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49838      * @return {Mixed} value The field value
49839      */
49840     getValue : function()
49841     {
49842         
49843         if (this.frame && this.frame.dom.style.display == 'none') {
49844             return Roo.form.FCKeditor.superclass.getValue.call(this);
49845         }
49846         
49847         if(!this.el || !this.getEditor()) {
49848            
49849            // this.getValue.defer(100,this); 
49850             return this.value;
49851         }
49852        
49853         
49854         var value=this.getEditor().GetData();
49855         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49856         return Roo.form.FCKeditor.superclass.getValue.call(this);
49857         
49858
49859     },
49860
49861     /**
49862      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49863      * @return {Mixed} value The field value
49864      */
49865     getRawValue : function()
49866     {
49867         if (this.frame && this.frame.dom.style.display == 'none') {
49868             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49869         }
49870         
49871         if(!this.el || !this.getEditor()) {
49872             //this.getRawValue.defer(100,this); 
49873             return this.value;
49874             return;
49875         }
49876         
49877         
49878         
49879         var value=this.getEditor().GetData();
49880         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49881         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49882          
49883     },
49884     
49885     setSize : function(w,h) {
49886         
49887         
49888         
49889         //if (this.frame && this.frame.dom.style.display == 'none') {
49890         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49891         //    return;
49892         //}
49893         //if(!this.el || !this.getEditor()) {
49894         //    this.setSize.defer(100,this, [w,h]); 
49895         //    return;
49896         //}
49897         
49898         
49899         
49900         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49901         
49902         this.frame.dom.setAttribute('width', w);
49903         this.frame.dom.setAttribute('height', h);
49904         this.frame.setSize(w,h);
49905         
49906     },
49907     
49908     toggleSourceEdit : function(value) {
49909         
49910       
49911          
49912         this.el.dom.style.display = value ? '' : 'none';
49913         this.frame.dom.style.display = value ?  'none' : '';
49914         
49915     },
49916     
49917     
49918     focus: function(tag)
49919     {
49920         if (this.frame.dom.style.display == 'none') {
49921             return Roo.form.FCKeditor.superclass.focus.call(this);
49922         }
49923         if(!this.el || !this.getEditor()) {
49924             this.focus.defer(100,this, [tag]); 
49925             return;
49926         }
49927         
49928         
49929         
49930         
49931         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49932         this.getEditor().Focus();
49933         if (tgs.length) {
49934             if (!this.getEditor().Selection.GetSelection()) {
49935                 this.focus.defer(100,this, [tag]); 
49936                 return;
49937             }
49938             
49939             
49940             var r = this.getEditor().EditorDocument.createRange();
49941             r.setStart(tgs[0],0);
49942             r.setEnd(tgs[0],0);
49943             this.getEditor().Selection.GetSelection().removeAllRanges();
49944             this.getEditor().Selection.GetSelection().addRange(r);
49945             this.getEditor().Focus();
49946         }
49947         
49948     },
49949     
49950     
49951     
49952     replaceTextarea : function()
49953     {
49954         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49955             return ;
49956         }
49957         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49958         //{
49959             // We must check the elements firstly using the Id and then the name.
49960         var oTextarea = document.getElementById( this.getId() );
49961         
49962         var colElementsByName = document.getElementsByName( this.getId() ) ;
49963          
49964         oTextarea.style.display = 'none' ;
49965
49966         if ( oTextarea.tabIndex ) {            
49967             this.TabIndex = oTextarea.tabIndex ;
49968         }
49969         
49970         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49971         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49972         this.frame = Roo.get(this.getId() + '___Frame')
49973     },
49974     
49975     _getConfigHtml : function()
49976     {
49977         var sConfig = '' ;
49978
49979         for ( var o in this.fckconfig ) {
49980             sConfig += sConfig.length > 0  ? '&amp;' : '';
49981             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49982         }
49983
49984         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49985     },
49986     
49987     
49988     _getIFrameHtml : function()
49989     {
49990         var sFile = 'fckeditor.html' ;
49991         /* no idea what this is about..
49992         try
49993         {
49994             if ( (/fcksource=true/i).test( window.top.location.search ) )
49995                 sFile = 'fckeditor.original.html' ;
49996         }
49997         catch (e) { 
49998         */
49999
50000         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50001         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50002         
50003         
50004         var html = '<iframe id="' + this.getId() +
50005             '___Frame" src="' + sLink +
50006             '" width="' + this.width +
50007             '" height="' + this.height + '"' +
50008             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50009             ' frameborder="0" scrolling="no"></iframe>' ;
50010
50011         return html ;
50012     },
50013     
50014     _insertHtmlBefore : function( html, element )
50015     {
50016         if ( element.insertAdjacentHTML )       {
50017             // IE
50018             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50019         } else { // Gecko
50020             var oRange = document.createRange() ;
50021             oRange.setStartBefore( element ) ;
50022             var oFragment = oRange.createContextualFragment( html );
50023             element.parentNode.insertBefore( oFragment, element ) ;
50024         }
50025     }
50026     
50027     
50028   
50029     
50030     
50031     
50032     
50033
50034 });
50035
50036 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50037
50038 function FCKeditor_OnComplete(editorInstance){
50039     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50040     f.fckEditor = editorInstance;
50041     //console.log("loaded");
50042     f.fireEvent('editorinit', f, editorInstance);
50043
50044   
50045
50046  
50047
50048
50049
50050
50051
50052
50053
50054
50055
50056
50057
50058
50059
50060
50061
50062 //<script type="text/javascript">
50063 /**
50064  * @class Roo.form.GridField
50065  * @extends Roo.form.Field
50066  * Embed a grid (or editable grid into a form)
50067  * STATUS ALPHA
50068  * 
50069  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50070  * it needs 
50071  * xgrid.store = Roo.data.Store
50072  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50073  * xgrid.store.reader = Roo.data.JsonReader 
50074  * 
50075  * 
50076  * @constructor
50077  * Creates a new GridField
50078  * @param {Object} config Configuration options
50079  */
50080 Roo.form.GridField = function(config){
50081     Roo.form.GridField.superclass.constructor.call(this, config);
50082      
50083 };
50084
50085 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50086     /**
50087      * @cfg {Number} width  - used to restrict width of grid..
50088      */
50089     width : 100,
50090     /**
50091      * @cfg {Number} height - used to restrict height of grid..
50092      */
50093     height : 50,
50094      /**
50095      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50096          * 
50097          *}
50098      */
50099     xgrid : false, 
50100     /**
50101      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50102      * {tag: "input", type: "checkbox", autocomplete: "off"})
50103      */
50104    // defaultAutoCreate : { tag: 'div' },
50105     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50106     /**
50107      * @cfg {String} addTitle Text to include for adding a title.
50108      */
50109     addTitle : false,
50110     //
50111     onResize : function(){
50112         Roo.form.Field.superclass.onResize.apply(this, arguments);
50113     },
50114
50115     initEvents : function(){
50116         // Roo.form.Checkbox.superclass.initEvents.call(this);
50117         // has no events...
50118        
50119     },
50120
50121
50122     getResizeEl : function(){
50123         return this.wrap;
50124     },
50125
50126     getPositionEl : function(){
50127         return this.wrap;
50128     },
50129
50130     // private
50131     onRender : function(ct, position){
50132         
50133         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50134         var style = this.style;
50135         delete this.style;
50136         
50137         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50138         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50139         this.viewEl = this.wrap.createChild({ tag: 'div' });
50140         if (style) {
50141             this.viewEl.applyStyles(style);
50142         }
50143         if (this.width) {
50144             this.viewEl.setWidth(this.width);
50145         }
50146         if (this.height) {
50147             this.viewEl.setHeight(this.height);
50148         }
50149         //if(this.inputValue !== undefined){
50150         //this.setValue(this.value);
50151         
50152         
50153         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50154         
50155         
50156         this.grid.render();
50157         this.grid.getDataSource().on('remove', this.refreshValue, this);
50158         this.grid.getDataSource().on('update', this.refreshValue, this);
50159         this.grid.on('afteredit', this.refreshValue, this);
50160  
50161     },
50162      
50163     
50164     /**
50165      * Sets the value of the item. 
50166      * @param {String} either an object  or a string..
50167      */
50168     setValue : function(v){
50169         //this.value = v;
50170         v = v || []; // empty set..
50171         // this does not seem smart - it really only affects memoryproxy grids..
50172         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50173             var ds = this.grid.getDataSource();
50174             // assumes a json reader..
50175             var data = {}
50176             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50177             ds.loadData( data);
50178         }
50179         // clear selection so it does not get stale.
50180         if (this.grid.sm) { 
50181             this.grid.sm.clearSelections();
50182         }
50183         
50184         Roo.form.GridField.superclass.setValue.call(this, v);
50185         this.refreshValue();
50186         // should load data in the grid really....
50187     },
50188     
50189     // private
50190     refreshValue: function() {
50191          var val = [];
50192         this.grid.getDataSource().each(function(r) {
50193             val.push(r.data);
50194         });
50195         this.el.dom.value = Roo.encode(val);
50196     }
50197     
50198      
50199     
50200     
50201 });/*
50202  * Based on:
50203  * Ext JS Library 1.1.1
50204  * Copyright(c) 2006-2007, Ext JS, LLC.
50205  *
50206  * Originally Released Under LGPL - original licence link has changed is not relivant.
50207  *
50208  * Fork - LGPL
50209  * <script type="text/javascript">
50210  */
50211 /**
50212  * @class Roo.form.DisplayField
50213  * @extends Roo.form.Field
50214  * A generic Field to display non-editable data.
50215  * @cfg {Boolean} closable (true|false) default false
50216  * @constructor
50217  * Creates a new Display Field item.
50218  * @param {Object} config Configuration options
50219  */
50220 Roo.form.DisplayField = function(config){
50221     Roo.form.DisplayField.superclass.constructor.call(this, config);
50222     
50223     this.addEvents({
50224         /**
50225          * @event close
50226          * Fires after the click the close btn
50227              * @param {Roo.form.DisplayField} this
50228              */
50229         close : true
50230     });
50231 };
50232
50233 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50234     inputType:      'hidden',
50235     allowBlank:     true,
50236     readOnly:         true,
50237     
50238  
50239     /**
50240      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50241      */
50242     focusClass : undefined,
50243     /**
50244      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50245      */
50246     fieldClass: 'x-form-field',
50247     
50248      /**
50249      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50250      */
50251     valueRenderer: undefined,
50252     
50253     width: 100,
50254     /**
50255      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50256      * {tag: "input", type: "checkbox", autocomplete: "off"})
50257      */
50258      
50259  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50260  
50261     closable : false,
50262     
50263     onResize : function(){
50264         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50265         
50266     },
50267
50268     initEvents : function(){
50269         // Roo.form.Checkbox.superclass.initEvents.call(this);
50270         // has no events...
50271         
50272         if(this.closable){
50273             this.closeEl.on('click', this.onClose, this);
50274         }
50275        
50276     },
50277
50278
50279     getResizeEl : function(){
50280         return this.wrap;
50281     },
50282
50283     getPositionEl : function(){
50284         return this.wrap;
50285     },
50286
50287     // private
50288     onRender : function(ct, position){
50289         
50290         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50291         //if(this.inputValue !== undefined){
50292         this.wrap = this.el.wrap();
50293         
50294         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50295         
50296         if(this.closable){
50297             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50298         }
50299         
50300         if (this.bodyStyle) {
50301             this.viewEl.applyStyles(this.bodyStyle);
50302         }
50303         //this.viewEl.setStyle('padding', '2px');
50304         
50305         this.setValue(this.value);
50306         
50307     },
50308 /*
50309     // private
50310     initValue : Roo.emptyFn,
50311
50312   */
50313
50314         // private
50315     onClick : function(){
50316         
50317     },
50318
50319     /**
50320      * Sets the checked state of the checkbox.
50321      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50322      */
50323     setValue : function(v){
50324         this.value = v;
50325         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50326         // this might be called before we have a dom element..
50327         if (!this.viewEl) {
50328             return;
50329         }
50330         this.viewEl.dom.innerHTML = html;
50331         Roo.form.DisplayField.superclass.setValue.call(this, v);
50332
50333     },
50334     
50335     onClose : function(e)
50336     {
50337         e.preventDefault();
50338         
50339         this.fireEvent('close', this);
50340     }
50341 });/*
50342  * 
50343  * Licence- LGPL
50344  * 
50345  */
50346
50347 /**
50348  * @class Roo.form.DayPicker
50349  * @extends Roo.form.Field
50350  * A Day picker show [M] [T] [W] ....
50351  * @constructor
50352  * Creates a new Day Picker
50353  * @param {Object} config Configuration options
50354  */
50355 Roo.form.DayPicker= function(config){
50356     Roo.form.DayPicker.superclass.constructor.call(this, config);
50357      
50358 };
50359
50360 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50361     /**
50362      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50363      */
50364     focusClass : undefined,
50365     /**
50366      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50367      */
50368     fieldClass: "x-form-field",
50369    
50370     /**
50371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50372      * {tag: "input", type: "checkbox", autocomplete: "off"})
50373      */
50374     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50375     
50376    
50377     actionMode : 'viewEl', 
50378     //
50379     // private
50380  
50381     inputType : 'hidden',
50382     
50383      
50384     inputElement: false, // real input element?
50385     basedOn: false, // ????
50386     
50387     isFormField: true, // not sure where this is needed!!!!
50388
50389     onResize : function(){
50390         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50391         if(!this.boxLabel){
50392             this.el.alignTo(this.wrap, 'c-c');
50393         }
50394     },
50395
50396     initEvents : function(){
50397         Roo.form.Checkbox.superclass.initEvents.call(this);
50398         this.el.on("click", this.onClick,  this);
50399         this.el.on("change", this.onClick,  this);
50400     },
50401
50402
50403     getResizeEl : function(){
50404         return this.wrap;
50405     },
50406
50407     getPositionEl : function(){
50408         return this.wrap;
50409     },
50410
50411     
50412     // private
50413     onRender : function(ct, position){
50414         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50415        
50416         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50417         
50418         var r1 = '<table><tr>';
50419         var r2 = '<tr class="x-form-daypick-icons">';
50420         for (var i=0; i < 7; i++) {
50421             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50422             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50423         }
50424         
50425         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50426         viewEl.select('img').on('click', this.onClick, this);
50427         this.viewEl = viewEl;   
50428         
50429         
50430         // this will not work on Chrome!!!
50431         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50432         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50433         
50434         
50435           
50436
50437     },
50438
50439     // private
50440     initValue : Roo.emptyFn,
50441
50442     /**
50443      * Returns the checked state of the checkbox.
50444      * @return {Boolean} True if checked, else false
50445      */
50446     getValue : function(){
50447         return this.el.dom.value;
50448         
50449     },
50450
50451         // private
50452     onClick : function(e){ 
50453         //this.setChecked(!this.checked);
50454         Roo.get(e.target).toggleClass('x-menu-item-checked');
50455         this.refreshValue();
50456         //if(this.el.dom.checked != this.checked){
50457         //    this.setValue(this.el.dom.checked);
50458        // }
50459     },
50460     
50461     // private
50462     refreshValue : function()
50463     {
50464         var val = '';
50465         this.viewEl.select('img',true).each(function(e,i,n)  {
50466             val += e.is(".x-menu-item-checked") ? String(n) : '';
50467         });
50468         this.setValue(val, true);
50469     },
50470
50471     /**
50472      * Sets the checked state of the checkbox.
50473      * On is always based on a string comparison between inputValue and the param.
50474      * @param {Boolean/String} value - the value to set 
50475      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50476      */
50477     setValue : function(v,suppressEvent){
50478         if (!this.el.dom) {
50479             return;
50480         }
50481         var old = this.el.dom.value ;
50482         this.el.dom.value = v;
50483         if (suppressEvent) {
50484             return ;
50485         }
50486          
50487         // update display..
50488         this.viewEl.select('img',true).each(function(e,i,n)  {
50489             
50490             var on = e.is(".x-menu-item-checked");
50491             var newv = v.indexOf(String(n)) > -1;
50492             if (on != newv) {
50493                 e.toggleClass('x-menu-item-checked');
50494             }
50495             
50496         });
50497         
50498         
50499         this.fireEvent('change', this, v, old);
50500         
50501         
50502     },
50503    
50504     // handle setting of hidden value by some other method!!?!?
50505     setFromHidden: function()
50506     {
50507         if(!this.el){
50508             return;
50509         }
50510         //console.log("SET FROM HIDDEN");
50511         //alert('setFrom hidden');
50512         this.setValue(this.el.dom.value);
50513     },
50514     
50515     onDestroy : function()
50516     {
50517         if(this.viewEl){
50518             Roo.get(this.viewEl).remove();
50519         }
50520          
50521         Roo.form.DayPicker.superclass.onDestroy.call(this);
50522     }
50523
50524 });/*
50525  * RooJS Library 1.1.1
50526  * Copyright(c) 2008-2011  Alan Knowles
50527  *
50528  * License - LGPL
50529  */
50530  
50531
50532 /**
50533  * @class Roo.form.ComboCheck
50534  * @extends Roo.form.ComboBox
50535  * A combobox for multiple select items.
50536  *
50537  * FIXME - could do with a reset button..
50538  * 
50539  * @constructor
50540  * Create a new ComboCheck
50541  * @param {Object} config Configuration options
50542  */
50543 Roo.form.ComboCheck = function(config){
50544     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50545     // should verify some data...
50546     // like
50547     // hiddenName = required..
50548     // displayField = required
50549     // valudField == required
50550     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50551     var _t = this;
50552     Roo.each(req, function(e) {
50553         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50554             throw "Roo.form.ComboCheck : missing value for: " + e;
50555         }
50556     });
50557     
50558     
50559 };
50560
50561 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50562      
50563      
50564     editable : false,
50565      
50566     selectedClass: 'x-menu-item-checked', 
50567     
50568     // private
50569     onRender : function(ct, position){
50570         var _t = this;
50571         
50572         
50573         
50574         if(!this.tpl){
50575             var cls = 'x-combo-list';
50576
50577             
50578             this.tpl =  new Roo.Template({
50579                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50580                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50581                    '<span>{' + this.displayField + '}</span>' +
50582                     '</div>' 
50583                 
50584             });
50585         }
50586  
50587         
50588         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50589         this.view.singleSelect = false;
50590         this.view.multiSelect = true;
50591         this.view.toggleSelect = true;
50592         this.pageTb.add(new Roo.Toolbar.Fill(), {
50593             
50594             text: 'Done',
50595             handler: function()
50596             {
50597                 _t.collapse();
50598             }
50599         });
50600     },
50601     
50602     onViewOver : function(e, t){
50603         // do nothing...
50604         return;
50605         
50606     },
50607     
50608     onViewClick : function(doFocus,index){
50609         return;
50610         
50611     },
50612     select: function () {
50613         //Roo.log("SELECT CALLED");
50614     },
50615      
50616     selectByValue : function(xv, scrollIntoView){
50617         var ar = this.getValueArray();
50618         var sels = [];
50619         
50620         Roo.each(ar, function(v) {
50621             if(v === undefined || v === null){
50622                 return;
50623             }
50624             var r = this.findRecord(this.valueField, v);
50625             if(r){
50626                 sels.push(this.store.indexOf(r))
50627                 
50628             }
50629         },this);
50630         this.view.select(sels);
50631         return false;
50632     },
50633     
50634     
50635     
50636     onSelect : function(record, index){
50637        // Roo.log("onselect Called");
50638        // this is only called by the clear button now..
50639         this.view.clearSelections();
50640         this.setValue('[]');
50641         if (this.value != this.valueBefore) {
50642             this.fireEvent('change', this, this.value, this.valueBefore);
50643             this.valueBefore = this.value;
50644         }
50645     },
50646     getValueArray : function()
50647     {
50648         var ar = [] ;
50649         
50650         try {
50651             //Roo.log(this.value);
50652             if (typeof(this.value) == 'undefined') {
50653                 return [];
50654             }
50655             var ar = Roo.decode(this.value);
50656             return  ar instanceof Array ? ar : []; //?? valid?
50657             
50658         } catch(e) {
50659             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50660             return [];
50661         }
50662          
50663     },
50664     expand : function ()
50665     {
50666         
50667         Roo.form.ComboCheck.superclass.expand.call(this);
50668         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50669         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50670         
50671
50672     },
50673     
50674     collapse : function(){
50675         Roo.form.ComboCheck.superclass.collapse.call(this);
50676         var sl = this.view.getSelectedIndexes();
50677         var st = this.store;
50678         var nv = [];
50679         var tv = [];
50680         var r;
50681         Roo.each(sl, function(i) {
50682             r = st.getAt(i);
50683             nv.push(r.get(this.valueField));
50684         },this);
50685         this.setValue(Roo.encode(nv));
50686         if (this.value != this.valueBefore) {
50687
50688             this.fireEvent('change', this, this.value, this.valueBefore);
50689             this.valueBefore = this.value;
50690         }
50691         
50692     },
50693     
50694     setValue : function(v){
50695         // Roo.log(v);
50696         this.value = v;
50697         
50698         var vals = this.getValueArray();
50699         var tv = [];
50700         Roo.each(vals, function(k) {
50701             var r = this.findRecord(this.valueField, k);
50702             if(r){
50703                 tv.push(r.data[this.displayField]);
50704             }else if(this.valueNotFoundText !== undefined){
50705                 tv.push( this.valueNotFoundText );
50706             }
50707         },this);
50708        // Roo.log(tv);
50709         
50710         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50711         this.hiddenField.value = v;
50712         this.value = v;
50713     }
50714     
50715 });/*
50716  * Based on:
50717  * Ext JS Library 1.1.1
50718  * Copyright(c) 2006-2007, Ext JS, LLC.
50719  *
50720  * Originally Released Under LGPL - original licence link has changed is not relivant.
50721  *
50722  * Fork - LGPL
50723  * <script type="text/javascript">
50724  */
50725  
50726 /**
50727  * @class Roo.form.Signature
50728  * @extends Roo.form.Field
50729  * Signature field.  
50730  * @constructor
50731  * 
50732  * @param {Object} config Configuration options
50733  */
50734
50735 Roo.form.Signature = function(config){
50736     Roo.form.Signature.superclass.constructor.call(this, config);
50737     
50738     this.addEvents({// not in used??
50739          /**
50740          * @event confirm
50741          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50742              * @param {Roo.form.Signature} combo This combo box
50743              */
50744         'confirm' : true,
50745         /**
50746          * @event reset
50747          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50748              * @param {Roo.form.ComboBox} combo This combo box
50749              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50750              */
50751         'reset' : true
50752     });
50753 };
50754
50755 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50756     /**
50757      * @cfg {Object} labels Label to use when rendering a form.
50758      * defaults to 
50759      * labels : { 
50760      *      clear : "Clear",
50761      *      confirm : "Confirm"
50762      *  }
50763      */
50764     labels : { 
50765         clear : "Clear",
50766         confirm : "Confirm"
50767     },
50768     /**
50769      * @cfg {Number} width The signature panel width (defaults to 300)
50770      */
50771     width: 300,
50772     /**
50773      * @cfg {Number} height The signature panel height (defaults to 100)
50774      */
50775     height : 100,
50776     /**
50777      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50778      */
50779     allowBlank : false,
50780     
50781     //private
50782     // {Object} signPanel The signature SVG panel element (defaults to {})
50783     signPanel : {},
50784     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50785     isMouseDown : false,
50786     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50787     isConfirmed : false,
50788     // {String} signatureTmp SVG mapping string (defaults to empty string)
50789     signatureTmp : '',
50790     
50791     
50792     defaultAutoCreate : { // modified by initCompnoent..
50793         tag: "input",
50794         type:"hidden"
50795     },
50796
50797     // private
50798     onRender : function(ct, position){
50799         
50800         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50801         
50802         this.wrap = this.el.wrap({
50803             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50804         });
50805         
50806         this.createToolbar(this);
50807         this.signPanel = this.wrap.createChild({
50808                 tag: 'div',
50809                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50810             }, this.el
50811         );
50812             
50813         this.svgID = Roo.id();
50814         this.svgEl = this.signPanel.createChild({
50815               xmlns : 'http://www.w3.org/2000/svg',
50816               tag : 'svg',
50817               id : this.svgID + "-svg",
50818               width: this.width,
50819               height: this.height,
50820               viewBox: '0 0 '+this.width+' '+this.height,
50821               cn : [
50822                 {
50823                     tag: "rect",
50824                     id: this.svgID + "-svg-r",
50825                     width: this.width,
50826                     height: this.height,
50827                     fill: "#ffa"
50828                 },
50829                 {
50830                     tag: "line",
50831                     id: this.svgID + "-svg-l",
50832                     x1: "0", // start
50833                     y1: (this.height*0.8), // start set the line in 80% of height
50834                     x2: this.width, // end
50835                     y2: (this.height*0.8), // end set the line in 80% of height
50836                     'stroke': "#666",
50837                     'stroke-width': "1",
50838                     'stroke-dasharray': "3",
50839                     'shape-rendering': "crispEdges",
50840                     'pointer-events': "none"
50841                 },
50842                 {
50843                     tag: "path",
50844                     id: this.svgID + "-svg-p",
50845                     'stroke': "navy",
50846                     'stroke-width': "3",
50847                     'fill': "none",
50848                     'pointer-events': 'none'
50849                 }
50850               ]
50851         });
50852         this.createSVG();
50853         this.svgBox = this.svgEl.dom.getScreenCTM();
50854     },
50855     createSVG : function(){ 
50856         var svg = this.signPanel;
50857         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50858         var t = this;
50859
50860         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50861         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50862         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50863         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50864         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50865         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50866         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50867         
50868     },
50869     isTouchEvent : function(e){
50870         return e.type.match(/^touch/);
50871     },
50872     getCoords : function (e) {
50873         var pt    = this.svgEl.dom.createSVGPoint();
50874         pt.x = e.clientX; 
50875         pt.y = e.clientY;
50876         if (this.isTouchEvent(e)) {
50877             pt.x =  e.targetTouches[0].clientX;
50878             pt.y = e.targetTouches[0].clientY;
50879         }
50880         var a = this.svgEl.dom.getScreenCTM();
50881         var b = a.inverse();
50882         var mx = pt.matrixTransform(b);
50883         return mx.x + ',' + mx.y;
50884     },
50885     //mouse event headler 
50886     down : function (e) {
50887         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50888         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50889         
50890         this.isMouseDown = true;
50891         
50892         e.preventDefault();
50893     },
50894     move : function (e) {
50895         if (this.isMouseDown) {
50896             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50897             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50898         }
50899         
50900         e.preventDefault();
50901     },
50902     up : function (e) {
50903         this.isMouseDown = false;
50904         var sp = this.signatureTmp.split(' ');
50905         
50906         if(sp.length > 1){
50907             if(!sp[sp.length-2].match(/^L/)){
50908                 sp.pop();
50909                 sp.pop();
50910                 sp.push("");
50911                 this.signatureTmp = sp.join(" ");
50912             }
50913         }
50914         if(this.getValue() != this.signatureTmp){
50915             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50916             this.isConfirmed = false;
50917         }
50918         e.preventDefault();
50919     },
50920     
50921     /**
50922      * Protected method that will not generally be called directly. It
50923      * is called when the editor creates its toolbar. Override this method if you need to
50924      * add custom toolbar buttons.
50925      * @param {HtmlEditor} editor
50926      */
50927     createToolbar : function(editor){
50928          function btn(id, toggle, handler){
50929             var xid = fid + '-'+ id ;
50930             return {
50931                 id : xid,
50932                 cmd : id,
50933                 cls : 'x-btn-icon x-edit-'+id,
50934                 enableToggle:toggle !== false,
50935                 scope: editor, // was editor...
50936                 handler:handler||editor.relayBtnCmd,
50937                 clickEvent:'mousedown',
50938                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50939                 tabIndex:-1
50940             };
50941         }
50942         
50943         
50944         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50945         this.tb = tb;
50946         this.tb.add(
50947            {
50948                 cls : ' x-signature-btn x-signature-'+id,
50949                 scope: editor, // was editor...
50950                 handler: this.reset,
50951                 clickEvent:'mousedown',
50952                 text: this.labels.clear
50953             },
50954             {
50955                  xtype : 'Fill',
50956                  xns: Roo.Toolbar
50957             }, 
50958             {
50959                 cls : '  x-signature-btn x-signature-'+id,
50960                 scope: editor, // was editor...
50961                 handler: this.confirmHandler,
50962                 clickEvent:'mousedown',
50963                 text: this.labels.confirm
50964             }
50965         );
50966     
50967     },
50968     //public
50969     /**
50970      * when user is clicked confirm then show this image.....
50971      * 
50972      * @return {String} Image Data URI
50973      */
50974     getImageDataURI : function(){
50975         var svg = this.svgEl.dom.parentNode.innerHTML;
50976         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50977         return src; 
50978     },
50979     /**
50980      * 
50981      * @return {Boolean} this.isConfirmed
50982      */
50983     getConfirmed : function(){
50984         return this.isConfirmed;
50985     },
50986     /**
50987      * 
50988      * @return {Number} this.width
50989      */
50990     getWidth : function(){
50991         return this.width;
50992     },
50993     /**
50994      * 
50995      * @return {Number} this.height
50996      */
50997     getHeight : function(){
50998         return this.height;
50999     },
51000     // private
51001     getSignature : function(){
51002         return this.signatureTmp;
51003     },
51004     // private
51005     reset : function(){
51006         this.signatureTmp = '';
51007         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51008         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51009         this.isConfirmed = false;
51010         Roo.form.Signature.superclass.reset.call(this);
51011     },
51012     setSignature : function(s){
51013         this.signatureTmp = s;
51014         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51015         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51016         this.setValue(s);
51017         this.isConfirmed = false;
51018         Roo.form.Signature.superclass.reset.call(this);
51019     }, 
51020     test : function(){
51021 //        Roo.log(this.signPanel.dom.contentWindow.up())
51022     },
51023     //private
51024     setConfirmed : function(){
51025         
51026         
51027         
51028 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51029     },
51030     // private
51031     confirmHandler : function(){
51032         if(!this.getSignature()){
51033             return;
51034         }
51035         
51036         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51037         this.setValue(this.getSignature());
51038         this.isConfirmed = true;
51039         
51040         this.fireEvent('confirm', this);
51041     },
51042     // private
51043     // Subclasses should provide the validation implementation by overriding this
51044     validateValue : function(value){
51045         if(this.allowBlank){
51046             return true;
51047         }
51048         
51049         if(this.isConfirmed){
51050             return true;
51051         }
51052         return false;
51053     }
51054 });/*
51055  * Based on:
51056  * Ext JS Library 1.1.1
51057  * Copyright(c) 2006-2007, Ext JS, LLC.
51058  *
51059  * Originally Released Under LGPL - original licence link has changed is not relivant.
51060  *
51061  * Fork - LGPL
51062  * <script type="text/javascript">
51063  */
51064  
51065
51066 /**
51067  * @class Roo.form.ComboBox
51068  * @extends Roo.form.TriggerField
51069  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51070  * @constructor
51071  * Create a new ComboBox.
51072  * @param {Object} config Configuration options
51073  */
51074 Roo.form.Select = function(config){
51075     Roo.form.Select.superclass.constructor.call(this, config);
51076      
51077 };
51078
51079 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51080     /**
51081      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51082      */
51083     /**
51084      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51085      * rendering into an Roo.Editor, defaults to false)
51086      */
51087     /**
51088      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51089      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51090      */
51091     /**
51092      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51093      */
51094     /**
51095      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51096      * the dropdown list (defaults to undefined, with no header element)
51097      */
51098
51099      /**
51100      * @cfg {String/Roo.Template} tpl The template to use to render the output
51101      */
51102      
51103     // private
51104     defaultAutoCreate : {tag: "select"  },
51105     /**
51106      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51107      */
51108     listWidth: undefined,
51109     /**
51110      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51111      * mode = 'remote' or 'text' if mode = 'local')
51112      */
51113     displayField: undefined,
51114     /**
51115      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51116      * mode = 'remote' or 'value' if mode = 'local'). 
51117      * Note: use of a valueField requires the user make a selection
51118      * in order for a value to be mapped.
51119      */
51120     valueField: undefined,
51121     
51122     
51123     /**
51124      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51125      * field's data value (defaults to the underlying DOM element's name)
51126      */
51127     hiddenName: undefined,
51128     /**
51129      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51130      */
51131     listClass: '',
51132     /**
51133      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51134      */
51135     selectedClass: 'x-combo-selected',
51136     /**
51137      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51139      * which displays a downward arrow icon).
51140      */
51141     triggerClass : 'x-form-arrow-trigger',
51142     /**
51143      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51144      */
51145     shadow:'sides',
51146     /**
51147      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51148      * anchor positions (defaults to 'tl-bl')
51149      */
51150     listAlign: 'tl-bl?',
51151     /**
51152      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51153      */
51154     maxHeight: 300,
51155     /**
51156      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51157      * query specified by the allQuery config option (defaults to 'query')
51158      */
51159     triggerAction: 'query',
51160     /**
51161      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51162      * (defaults to 4, does not apply if editable = false)
51163      */
51164     minChars : 4,
51165     /**
51166      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51167      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51168      */
51169     typeAhead: false,
51170     /**
51171      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51172      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51173      */
51174     queryDelay: 500,
51175     /**
51176      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51177      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51178      */
51179     pageSize: 0,
51180     /**
51181      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51182      * when editable = true (defaults to false)
51183      */
51184     selectOnFocus:false,
51185     /**
51186      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51187      */
51188     queryParam: 'query',
51189     /**
51190      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51191      * when mode = 'remote' (defaults to 'Loading...')
51192      */
51193     loadingText: 'Loading...',
51194     /**
51195      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51196      */
51197     resizable: false,
51198     /**
51199      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51200      */
51201     handleHeight : 8,
51202     /**
51203      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51204      * traditional select (defaults to true)
51205      */
51206     editable: true,
51207     /**
51208      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51209      */
51210     allQuery: '',
51211     /**
51212      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51213      */
51214     mode: 'remote',
51215     /**
51216      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51217      * listWidth has a higher value)
51218      */
51219     minListWidth : 70,
51220     /**
51221      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51222      * allow the user to set arbitrary text into the field (defaults to false)
51223      */
51224     forceSelection:false,
51225     /**
51226      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51227      * if typeAhead = true (defaults to 250)
51228      */
51229     typeAheadDelay : 250,
51230     /**
51231      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51232      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51233      */
51234     valueNotFoundText : undefined,
51235     
51236     /**
51237      * @cfg {String} defaultValue The value displayed after loading the store.
51238      */
51239     defaultValue: '',
51240     
51241     /**
51242      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51243      */
51244     blockFocus : false,
51245     
51246     /**
51247      * @cfg {Boolean} disableClear Disable showing of clear button.
51248      */
51249     disableClear : false,
51250     /**
51251      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51252      */
51253     alwaysQuery : false,
51254     
51255     //private
51256     addicon : false,
51257     editicon: false,
51258     
51259     // element that contains real text value.. (when hidden is used..)
51260      
51261     // private
51262     onRender : function(ct, position){
51263         Roo.form.Field.prototype.onRender.call(this, ct, position);
51264         
51265         if(this.store){
51266             this.store.on('beforeload', this.onBeforeLoad, this);
51267             this.store.on('load', this.onLoad, this);
51268             this.store.on('loadexception', this.onLoadException, this);
51269             this.store.load({});
51270         }
51271         
51272         
51273         
51274     },
51275
51276     // private
51277     initEvents : function(){
51278         //Roo.form.ComboBox.superclass.initEvents.call(this);
51279  
51280     },
51281
51282     onDestroy : function(){
51283        
51284         if(this.store){
51285             this.store.un('beforeload', this.onBeforeLoad, this);
51286             this.store.un('load', this.onLoad, this);
51287             this.store.un('loadexception', this.onLoadException, this);
51288         }
51289         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51290     },
51291
51292     // private
51293     fireKey : function(e){
51294         if(e.isNavKeyPress() && !this.list.isVisible()){
51295             this.fireEvent("specialkey", this, e);
51296         }
51297     },
51298
51299     // private
51300     onResize: function(w, h){
51301         
51302         return; 
51303     
51304         
51305     },
51306
51307     /**
51308      * Allow or prevent the user from directly editing the field text.  If false is passed,
51309      * the user will only be able to select from the items defined in the dropdown list.  This method
51310      * is the runtime equivalent of setting the 'editable' config option at config time.
51311      * @param {Boolean} value True to allow the user to directly edit the field text
51312      */
51313     setEditable : function(value){
51314          
51315     },
51316
51317     // private
51318     onBeforeLoad : function(){
51319         
51320         Roo.log("Select before load");
51321         return;
51322     
51323         this.innerList.update(this.loadingText ?
51324                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51325         //this.restrictHeight();
51326         this.selectedIndex = -1;
51327     },
51328
51329     // private
51330     onLoad : function(){
51331
51332     
51333         var dom = this.el.dom;
51334         dom.innerHTML = '';
51335          var od = dom.ownerDocument;
51336          
51337         if (this.emptyText) {
51338             var op = od.createElement('option');
51339             op.setAttribute('value', '');
51340             op.innerHTML = String.format('{0}', this.emptyText);
51341             dom.appendChild(op);
51342         }
51343         if(this.store.getCount() > 0){
51344            
51345             var vf = this.valueField;
51346             var df = this.displayField;
51347             this.store.data.each(function(r) {
51348                 // which colmsn to use... testing - cdoe / title..
51349                 var op = od.createElement('option');
51350                 op.setAttribute('value', r.data[vf]);
51351                 op.innerHTML = String.format('{0}', r.data[df]);
51352                 dom.appendChild(op);
51353             });
51354             if (typeof(this.defaultValue != 'undefined')) {
51355                 this.setValue(this.defaultValue);
51356             }
51357             
51358              
51359         }else{
51360             //this.onEmptyResults();
51361         }
51362         //this.el.focus();
51363     },
51364     // private
51365     onLoadException : function()
51366     {
51367         dom.innerHTML = '';
51368             
51369         Roo.log("Select on load exception");
51370         return;
51371     
51372         this.collapse();
51373         Roo.log(this.store.reader.jsonData);
51374         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51375             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51376         }
51377         
51378         
51379     },
51380     // private
51381     onTypeAhead : function(){
51382          
51383     },
51384
51385     // private
51386     onSelect : function(record, index){
51387         Roo.log('on select?');
51388         return;
51389         if(this.fireEvent('beforeselect', this, record, index) !== false){
51390             this.setFromData(index > -1 ? record.data : false);
51391             this.collapse();
51392             this.fireEvent('select', this, record, index);
51393         }
51394     },
51395
51396     /**
51397      * Returns the currently selected field value or empty string if no value is set.
51398      * @return {String} value The selected value
51399      */
51400     getValue : function(){
51401         var dom = this.el.dom;
51402         this.value = dom.options[dom.selectedIndex].value;
51403         return this.value;
51404         
51405     },
51406
51407     /**
51408      * Clears any text/value currently set in the field
51409      */
51410     clearValue : function(){
51411         this.value = '';
51412         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51413         
51414     },
51415
51416     /**
51417      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51418      * will be displayed in the field.  If the value does not match the data value of an existing item,
51419      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51420      * Otherwise the field will be blank (although the value will still be set).
51421      * @param {String} value The value to match
51422      */
51423     setValue : function(v){
51424         var d = this.el.dom;
51425         for (var i =0; i < d.options.length;i++) {
51426             if (v == d.options[i].value) {
51427                 d.selectedIndex = i;
51428                 this.value = v;
51429                 return;
51430             }
51431         }
51432         this.clearValue();
51433     },
51434     /**
51435      * @property {Object} the last set data for the element
51436      */
51437     
51438     lastData : false,
51439     /**
51440      * Sets the value of the field based on a object which is related to the record format for the store.
51441      * @param {Object} value the value to set as. or false on reset?
51442      */
51443     setFromData : function(o){
51444         Roo.log('setfrom data?');
51445          
51446         
51447         
51448     },
51449     // private
51450     reset : function(){
51451         this.clearValue();
51452     },
51453     // private
51454     findRecord : function(prop, value){
51455         
51456         return false;
51457     
51458         var record;
51459         if(this.store.getCount() > 0){
51460             this.store.each(function(r){
51461                 if(r.data[prop] == value){
51462                     record = r;
51463                     return false;
51464                 }
51465                 return true;
51466             });
51467         }
51468         return record;
51469     },
51470     
51471     getName: function()
51472     {
51473         // returns hidden if it's set..
51474         if (!this.rendered) {return ''};
51475         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51476         
51477     },
51478      
51479
51480     
51481
51482     // private
51483     onEmptyResults : function(){
51484         Roo.log('empty results');
51485         //this.collapse();
51486     },
51487
51488     /**
51489      * Returns true if the dropdown list is expanded, else false.
51490      */
51491     isExpanded : function(){
51492         return false;
51493     },
51494
51495     /**
51496      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51497      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51498      * @param {String} value The data value of the item to select
51499      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51500      * selected item if it is not currently in view (defaults to true)
51501      * @return {Boolean} True if the value matched an item in the list, else false
51502      */
51503     selectByValue : function(v, scrollIntoView){
51504         Roo.log('select By Value');
51505         return false;
51506     
51507         if(v !== undefined && v !== null){
51508             var r = this.findRecord(this.valueField || this.displayField, v);
51509             if(r){
51510                 this.select(this.store.indexOf(r), scrollIntoView);
51511                 return true;
51512             }
51513         }
51514         return false;
51515     },
51516
51517     /**
51518      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51519      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51520      * @param {Number} index The zero-based index of the list item to select
51521      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51522      * selected item if it is not currently in view (defaults to true)
51523      */
51524     select : function(index, scrollIntoView){
51525         Roo.log('select ');
51526         return  ;
51527         
51528         this.selectedIndex = index;
51529         this.view.select(index);
51530         if(scrollIntoView !== false){
51531             var el = this.view.getNode(index);
51532             if(el){
51533                 this.innerList.scrollChildIntoView(el, false);
51534             }
51535         }
51536     },
51537
51538       
51539
51540     // private
51541     validateBlur : function(){
51542         
51543         return;
51544         
51545     },
51546
51547     // private
51548     initQuery : function(){
51549         this.doQuery(this.getRawValue());
51550     },
51551
51552     // private
51553     doForce : function(){
51554         if(this.el.dom.value.length > 0){
51555             this.el.dom.value =
51556                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51557              
51558         }
51559     },
51560
51561     /**
51562      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51563      * query allowing the query action to be canceled if needed.
51564      * @param {String} query The SQL query to execute
51565      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51566      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51567      * saved in the current store (defaults to false)
51568      */
51569     doQuery : function(q, forceAll){
51570         
51571         Roo.log('doQuery?');
51572         if(q === undefined || q === null){
51573             q = '';
51574         }
51575         var qe = {
51576             query: q,
51577             forceAll: forceAll,
51578             combo: this,
51579             cancel:false
51580         };
51581         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51582             return false;
51583         }
51584         q = qe.query;
51585         forceAll = qe.forceAll;
51586         if(forceAll === true || (q.length >= this.minChars)){
51587             if(this.lastQuery != q || this.alwaysQuery){
51588                 this.lastQuery = q;
51589                 if(this.mode == 'local'){
51590                     this.selectedIndex = -1;
51591                     if(forceAll){
51592                         this.store.clearFilter();
51593                     }else{
51594                         this.store.filter(this.displayField, q);
51595                     }
51596                     this.onLoad();
51597                 }else{
51598                     this.store.baseParams[this.queryParam] = q;
51599                     this.store.load({
51600                         params: this.getParams(q)
51601                     });
51602                     this.expand();
51603                 }
51604             }else{
51605                 this.selectedIndex = -1;
51606                 this.onLoad();   
51607             }
51608         }
51609     },
51610
51611     // private
51612     getParams : function(q){
51613         var p = {};
51614         //p[this.queryParam] = q;
51615         if(this.pageSize){
51616             p.start = 0;
51617             p.limit = this.pageSize;
51618         }
51619         return p;
51620     },
51621
51622     /**
51623      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51624      */
51625     collapse : function(){
51626         
51627     },
51628
51629     // private
51630     collapseIf : function(e){
51631         
51632     },
51633
51634     /**
51635      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51636      */
51637     expand : function(){
51638         
51639     } ,
51640
51641     // private
51642      
51643
51644     /** 
51645     * @cfg {Boolean} grow 
51646     * @hide 
51647     */
51648     /** 
51649     * @cfg {Number} growMin 
51650     * @hide 
51651     */
51652     /** 
51653     * @cfg {Number} growMax 
51654     * @hide 
51655     */
51656     /**
51657      * @hide
51658      * @method autoSize
51659      */
51660     
51661     setWidth : function()
51662     {
51663         
51664     },
51665     getResizeEl : function(){
51666         return this.el;
51667     }
51668 });//<script type="text/javasscript">
51669  
51670
51671 /**
51672  * @class Roo.DDView
51673  * A DnD enabled version of Roo.View.
51674  * @param {Element/String} container The Element in which to create the View.
51675  * @param {String} tpl The template string used to create the markup for each element of the View
51676  * @param {Object} config The configuration properties. These include all the config options of
51677  * {@link Roo.View} plus some specific to this class.<br>
51678  * <p>
51679  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51680  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51681  * <p>
51682  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51683 .x-view-drag-insert-above {
51684         border-top:1px dotted #3366cc;
51685 }
51686 .x-view-drag-insert-below {
51687         border-bottom:1px dotted #3366cc;
51688 }
51689 </code></pre>
51690  * 
51691  */
51692  
51693 Roo.DDView = function(container, tpl, config) {
51694     Roo.DDView.superclass.constructor.apply(this, arguments);
51695     this.getEl().setStyle("outline", "0px none");
51696     this.getEl().unselectable();
51697     if (this.dragGroup) {
51698         this.setDraggable(this.dragGroup.split(","));
51699     }
51700     if (this.dropGroup) {
51701         this.setDroppable(this.dropGroup.split(","));
51702     }
51703     if (this.deletable) {
51704         this.setDeletable();
51705     }
51706     this.isDirtyFlag = false;
51707         this.addEvents({
51708                 "drop" : true
51709         });
51710 };
51711
51712 Roo.extend(Roo.DDView, Roo.View, {
51713 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51714 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51715 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51716 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51717
51718         isFormField: true,
51719
51720         reset: Roo.emptyFn,
51721         
51722         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51723
51724         validate: function() {
51725                 return true;
51726         },
51727         
51728         destroy: function() {
51729                 this.purgeListeners();
51730                 this.getEl.removeAllListeners();
51731                 this.getEl().remove();
51732                 if (this.dragZone) {
51733                         if (this.dragZone.destroy) {
51734                                 this.dragZone.destroy();
51735                         }
51736                 }
51737                 if (this.dropZone) {
51738                         if (this.dropZone.destroy) {
51739                                 this.dropZone.destroy();
51740                         }
51741                 }
51742         },
51743
51744 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51745         getName: function() {
51746                 return this.name;
51747         },
51748
51749 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51750         setValue: function(v) {
51751                 if (!this.store) {
51752                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51753                 }
51754                 var data = {};
51755                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51756                 this.store.proxy = new Roo.data.MemoryProxy(data);
51757                 this.store.load();
51758         },
51759
51760 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51761         getValue: function() {
51762                 var result = '(';
51763                 this.store.each(function(rec) {
51764                         result += rec.id + ',';
51765                 });
51766                 return result.substr(0, result.length - 1) + ')';
51767         },
51768         
51769         getIds: function() {
51770                 var i = 0, result = new Array(this.store.getCount());
51771                 this.store.each(function(rec) {
51772                         result[i++] = rec.id;
51773                 });
51774                 return result;
51775         },
51776         
51777         isDirty: function() {
51778                 return this.isDirtyFlag;
51779         },
51780
51781 /**
51782  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51783  *      whole Element becomes the target, and this causes the drop gesture to append.
51784  */
51785     getTargetFromEvent : function(e) {
51786                 var target = e.getTarget();
51787                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51788                 target = target.parentNode;
51789                 }
51790                 if (!target) {
51791                         target = this.el.dom.lastChild || this.el.dom;
51792                 }
51793                 return target;
51794     },
51795
51796 /**
51797  *      Create the drag data which consists of an object which has the property "ddel" as
51798  *      the drag proxy element. 
51799  */
51800     getDragData : function(e) {
51801         var target = this.findItemFromChild(e.getTarget());
51802                 if(target) {
51803                         this.handleSelection(e);
51804                         var selNodes = this.getSelectedNodes();
51805             var dragData = {
51806                 source: this,
51807                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51808                 nodes: selNodes,
51809                 records: []
51810                         };
51811                         var selectedIndices = this.getSelectedIndexes();
51812                         for (var i = 0; i < selectedIndices.length; i++) {
51813                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51814                         }
51815                         if (selNodes.length == 1) {
51816                                 dragData.ddel = target.cloneNode(true); // the div element
51817                         } else {
51818                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51819                                 div.className = 'multi-proxy';
51820                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51821                                         div.appendChild(selNodes[i].cloneNode(true));
51822                                 }
51823                                 dragData.ddel = div;
51824                         }
51825             //console.log(dragData)
51826             //console.log(dragData.ddel.innerHTML)
51827                         return dragData;
51828                 }
51829         //console.log('nodragData')
51830                 return false;
51831     },
51832     
51833 /**     Specify to which ddGroup items in this DDView may be dragged. */
51834     setDraggable: function(ddGroup) {
51835         if (ddGroup instanceof Array) {
51836                 Roo.each(ddGroup, this.setDraggable, this);
51837                 return;
51838         }
51839         if (this.dragZone) {
51840                 this.dragZone.addToGroup(ddGroup);
51841         } else {
51842                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51843                                 containerScroll: true,
51844                                 ddGroup: ddGroup 
51845
51846                         });
51847 //                      Draggability implies selection. DragZone's mousedown selects the element.
51848                         if (!this.multiSelect) { this.singleSelect = true; }
51849
51850 //                      Wire the DragZone's handlers up to methods in *this*
51851                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51852                 }
51853     },
51854
51855 /**     Specify from which ddGroup this DDView accepts drops. */
51856     setDroppable: function(ddGroup) {
51857         if (ddGroup instanceof Array) {
51858                 Roo.each(ddGroup, this.setDroppable, this);
51859                 return;
51860         }
51861         if (this.dropZone) {
51862                 this.dropZone.addToGroup(ddGroup);
51863         } else {
51864                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51865                                 containerScroll: true,
51866                                 ddGroup: ddGroup
51867                         });
51868
51869 //                      Wire the DropZone's handlers up to methods in *this*
51870                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51871                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51872                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51873                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51874                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51875                 }
51876     },
51877
51878 /**     Decide whether to drop above or below a View node. */
51879     getDropPoint : function(e, n, dd){
51880         if (n == this.el.dom) { return "above"; }
51881                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51882                 var c = t + (b - t) / 2;
51883                 var y = Roo.lib.Event.getPageY(e);
51884                 if(y <= c) {
51885                         return "above";
51886                 }else{
51887                         return "below";
51888                 }
51889     },
51890
51891     onNodeEnter : function(n, dd, e, data){
51892                 return false;
51893     },
51894     
51895     onNodeOver : function(n, dd, e, data){
51896                 var pt = this.getDropPoint(e, n, dd);
51897                 // set the insert point style on the target node
51898                 var dragElClass = this.dropNotAllowed;
51899                 if (pt) {
51900                         var targetElClass;
51901                         if (pt == "above"){
51902                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51903                                 targetElClass = "x-view-drag-insert-above";
51904                         } else {
51905                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51906                                 targetElClass = "x-view-drag-insert-below";
51907                         }
51908                         if (this.lastInsertClass != targetElClass){
51909                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51910                                 this.lastInsertClass = targetElClass;
51911                         }
51912                 }
51913                 return dragElClass;
51914         },
51915
51916     onNodeOut : function(n, dd, e, data){
51917                 this.removeDropIndicators(n);
51918     },
51919
51920     onNodeDrop : function(n, dd, e, data){
51921         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51922                 return false;
51923         }
51924         var pt = this.getDropPoint(e, n, dd);
51925                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51926                 if (pt == "below") { insertAt++; }
51927                 for (var i = 0; i < data.records.length; i++) {
51928                         var r = data.records[i];
51929                         var dup = this.store.getById(r.id);
51930                         if (dup && (dd != this.dragZone)) {
51931                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51932                         } else {
51933                                 if (data.copy) {
51934                                         this.store.insert(insertAt++, r.copy());
51935                                 } else {
51936                                         data.source.isDirtyFlag = true;
51937                                         r.store.remove(r);
51938                                         this.store.insert(insertAt++, r);
51939                                 }
51940                                 this.isDirtyFlag = true;
51941                         }
51942                 }
51943                 this.dragZone.cachedTarget = null;
51944                 return true;
51945     },
51946
51947     removeDropIndicators : function(n){
51948                 if(n){
51949                         Roo.fly(n).removeClass([
51950                                 "x-view-drag-insert-above",
51951                                 "x-view-drag-insert-below"]);
51952                         this.lastInsertClass = "_noclass";
51953                 }
51954     },
51955
51956 /**
51957  *      Utility method. Add a delete option to the DDView's context menu.
51958  *      @param {String} imageUrl The URL of the "delete" icon image.
51959  */
51960         setDeletable: function(imageUrl) {
51961                 if (!this.singleSelect && !this.multiSelect) {
51962                         this.singleSelect = true;
51963                 }
51964                 var c = this.getContextMenu();
51965                 this.contextMenu.on("itemclick", function(item) {
51966                         switch (item.id) {
51967                                 case "delete":
51968                                         this.remove(this.getSelectedIndexes());
51969                                         break;
51970                         }
51971                 }, this);
51972                 this.contextMenu.add({
51973                         icon: imageUrl,
51974                         id: "delete",
51975                         text: 'Delete'
51976                 });
51977         },
51978         
51979 /**     Return the context menu for this DDView. */
51980         getContextMenu: function() {
51981                 if (!this.contextMenu) {
51982 //                      Create the View's context menu
51983                         this.contextMenu = new Roo.menu.Menu({
51984                                 id: this.id + "-contextmenu"
51985                         });
51986                         this.el.on("contextmenu", this.showContextMenu, this);
51987                 }
51988                 return this.contextMenu;
51989         },
51990         
51991         disableContextMenu: function() {
51992                 if (this.contextMenu) {
51993                         this.el.un("contextmenu", this.showContextMenu, this);
51994                 }
51995         },
51996
51997         showContextMenu: function(e, item) {
51998         item = this.findItemFromChild(e.getTarget());
51999                 if (item) {
52000                         e.stopEvent();
52001                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52002                         this.contextMenu.showAt(e.getXY());
52003             }
52004     },
52005
52006 /**
52007  *      Remove {@link Roo.data.Record}s at the specified indices.
52008  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52009  */
52010     remove: function(selectedIndices) {
52011                 selectedIndices = [].concat(selectedIndices);
52012                 for (var i = 0; i < selectedIndices.length; i++) {
52013                         var rec = this.store.getAt(selectedIndices[i]);
52014                         this.store.remove(rec);
52015                 }
52016     },
52017
52018 /**
52019  *      Double click fires the event, but also, if this is draggable, and there is only one other
52020  *      related DropZone, it transfers the selected node.
52021  */
52022     onDblClick : function(e){
52023         var item = this.findItemFromChild(e.getTarget());
52024         if(item){
52025             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52026                 return false;
52027             }
52028             if (this.dragGroup) {
52029                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52030                     while (targets.indexOf(this.dropZone) > -1) {
52031                             targets.remove(this.dropZone);
52032                                 }
52033                     if (targets.length == 1) {
52034                                         this.dragZone.cachedTarget = null;
52035                         var el = Roo.get(targets[0].getEl());
52036                         var box = el.getBox(true);
52037                         targets[0].onNodeDrop(el.dom, {
52038                                 target: el.dom,
52039                                 xy: [box.x, box.y + box.height - 1]
52040                         }, null, this.getDragData(e));
52041                     }
52042                 }
52043         }
52044     },
52045     
52046     handleSelection: function(e) {
52047                 this.dragZone.cachedTarget = null;
52048         var item = this.findItemFromChild(e.getTarget());
52049         if (!item) {
52050                 this.clearSelections(true);
52051                 return;
52052         }
52053                 if (item && (this.multiSelect || this.singleSelect)){
52054                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52055                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52056                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52057                                 this.unselect(item);
52058                         } else {
52059                                 this.select(item, this.multiSelect && e.ctrlKey);
52060                                 this.lastSelection = item;
52061                         }
52062                 }
52063     },
52064
52065     onItemClick : function(item, index, e){
52066                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52067                         return false;
52068                 }
52069                 return true;
52070     },
52071
52072     unselect : function(nodeInfo, suppressEvent){
52073                 var node = this.getNode(nodeInfo);
52074                 if(node && this.isSelected(node)){
52075                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52076                                 Roo.fly(node).removeClass(this.selectedClass);
52077                                 this.selections.remove(node);
52078                                 if(!suppressEvent){
52079                                         this.fireEvent("selectionchange", this, this.selections);
52080                                 }
52081                         }
52082                 }
52083     }
52084 });
52085 /*
52086  * Based on:
52087  * Ext JS Library 1.1.1
52088  * Copyright(c) 2006-2007, Ext JS, LLC.
52089  *
52090  * Originally Released Under LGPL - original licence link has changed is not relivant.
52091  *
52092  * Fork - LGPL
52093  * <script type="text/javascript">
52094  */
52095  
52096 /**
52097  * @class Roo.LayoutManager
52098  * @extends Roo.util.Observable
52099  * Base class for layout managers.
52100  */
52101 Roo.LayoutManager = function(container, config){
52102     Roo.LayoutManager.superclass.constructor.call(this);
52103     this.el = Roo.get(container);
52104     // ie scrollbar fix
52105     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52106         document.body.scroll = "no";
52107     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52108         this.el.position('relative');
52109     }
52110     this.id = this.el.id;
52111     this.el.addClass("x-layout-container");
52112     /** false to disable window resize monitoring @type Boolean */
52113     this.monitorWindowResize = true;
52114     this.regions = {};
52115     this.addEvents({
52116         /**
52117          * @event layout
52118          * Fires when a layout is performed. 
52119          * @param {Roo.LayoutManager} this
52120          */
52121         "layout" : true,
52122         /**
52123          * @event regionresized
52124          * Fires when the user resizes a region. 
52125          * @param {Roo.LayoutRegion} region The resized region
52126          * @param {Number} newSize The new size (width for east/west, height for north/south)
52127          */
52128         "regionresized" : true,
52129         /**
52130          * @event regioncollapsed
52131          * Fires when a region is collapsed. 
52132          * @param {Roo.LayoutRegion} region The collapsed region
52133          */
52134         "regioncollapsed" : true,
52135         /**
52136          * @event regionexpanded
52137          * Fires when a region is expanded.  
52138          * @param {Roo.LayoutRegion} region The expanded region
52139          */
52140         "regionexpanded" : true
52141     });
52142     this.updating = false;
52143     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52144 };
52145
52146 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52147     /**
52148      * Returns true if this layout is currently being updated
52149      * @return {Boolean}
52150      */
52151     isUpdating : function(){
52152         return this.updating; 
52153     },
52154     
52155     /**
52156      * Suspend the LayoutManager from doing auto-layouts while
52157      * making multiple add or remove calls
52158      */
52159     beginUpdate : function(){
52160         this.updating = true;    
52161     },
52162     
52163     /**
52164      * Restore auto-layouts and optionally disable the manager from performing a layout
52165      * @param {Boolean} noLayout true to disable a layout update 
52166      */
52167     endUpdate : function(noLayout){
52168         this.updating = false;
52169         if(!noLayout){
52170             this.layout();
52171         }    
52172     },
52173     
52174     layout: function(){
52175         
52176     },
52177     
52178     onRegionResized : function(region, newSize){
52179         this.fireEvent("regionresized", region, newSize);
52180         this.layout();
52181     },
52182     
52183     onRegionCollapsed : function(region){
52184         this.fireEvent("regioncollapsed", region);
52185     },
52186     
52187     onRegionExpanded : function(region){
52188         this.fireEvent("regionexpanded", region);
52189     },
52190         
52191     /**
52192      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52193      * performs box-model adjustments.
52194      * @return {Object} The size as an object {width: (the width), height: (the height)}
52195      */
52196     getViewSize : function(){
52197         var size;
52198         if(this.el.dom != document.body){
52199             size = this.el.getSize();
52200         }else{
52201             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52202         }
52203         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52204         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52205         return size;
52206     },
52207     
52208     /**
52209      * Returns the Element this layout is bound to.
52210      * @return {Roo.Element}
52211      */
52212     getEl : function(){
52213         return this.el;
52214     },
52215     
52216     /**
52217      * Returns the specified region.
52218      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52219      * @return {Roo.LayoutRegion}
52220      */
52221     getRegion : function(target){
52222         return this.regions[target.toLowerCase()];
52223     },
52224     
52225     onWindowResize : function(){
52226         if(this.monitorWindowResize){
52227             this.layout();
52228         }
52229     }
52230 });/*
52231  * Based on:
52232  * Ext JS Library 1.1.1
52233  * Copyright(c) 2006-2007, Ext JS, LLC.
52234  *
52235  * Originally Released Under LGPL - original licence link has changed is not relivant.
52236  *
52237  * Fork - LGPL
52238  * <script type="text/javascript">
52239  */
52240 /**
52241  * @class Roo.BorderLayout
52242  * @extends Roo.LayoutManager
52243  * @children Roo.ContentPanel
52244  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52245  * please see: <br><br>
52246  * <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>
52247  * <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>
52248  * Example:
52249  <pre><code>
52250  var layout = new Roo.BorderLayout(document.body, {
52251     north: {
52252         initialSize: 25,
52253         titlebar: false
52254     },
52255     west: {
52256         split:true,
52257         initialSize: 200,
52258         minSize: 175,
52259         maxSize: 400,
52260         titlebar: true,
52261         collapsible: true
52262     },
52263     east: {
52264         split:true,
52265         initialSize: 202,
52266         minSize: 175,
52267         maxSize: 400,
52268         titlebar: true,
52269         collapsible: true
52270     },
52271     south: {
52272         split:true,
52273         initialSize: 100,
52274         minSize: 100,
52275         maxSize: 200,
52276         titlebar: true,
52277         collapsible: true
52278     },
52279     center: {
52280         titlebar: true,
52281         autoScroll:true,
52282         resizeTabs: true,
52283         minTabWidth: 50,
52284         preferredTabWidth: 150
52285     }
52286 });
52287
52288 // shorthand
52289 var CP = Roo.ContentPanel;
52290
52291 layout.beginUpdate();
52292 layout.add("north", new CP("north", "North"));
52293 layout.add("south", new CP("south", {title: "South", closable: true}));
52294 layout.add("west", new CP("west", {title: "West"}));
52295 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52296 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52297 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52298 layout.getRegion("center").showPanel("center1");
52299 layout.endUpdate();
52300 </code></pre>
52301
52302 <b>The container the layout is rendered into can be either the body element or any other element.
52303 If it is not the body element, the container needs to either be an absolute positioned element,
52304 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52305 the container size if it is not the body element.</b>
52306
52307 * @constructor
52308 * Create a new BorderLayout
52309 * @param {String/HTMLElement/Element} container The container this layout is bound to
52310 * @param {Object} config Configuration options
52311  */
52312 Roo.BorderLayout = function(container, config){
52313     config = config || {};
52314     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52315     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52316     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52317         var target = this.factory.validRegions[i];
52318         if(config[target]){
52319             this.addRegion(target, config[target]);
52320         }
52321     }
52322 };
52323
52324 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52325         
52326         /**
52327          * @cfg {Roo.LayoutRegion} east
52328          */
52329         /**
52330          * @cfg {Roo.LayoutRegion} west
52331          */
52332         /**
52333          * @cfg {Roo.LayoutRegion} north
52334          */
52335         /**
52336          * @cfg {Roo.LayoutRegion} south
52337          */
52338         /**
52339          * @cfg {Roo.LayoutRegion} center
52340          */
52341     /**
52342      * Creates and adds a new region if it doesn't already exist.
52343      * @param {String} target The target region key (north, south, east, west or center).
52344      * @param {Object} config The regions config object
52345      * @return {BorderLayoutRegion} The new region
52346      */
52347     addRegion : function(target, config){
52348         if(!this.regions[target]){
52349             var r = this.factory.create(target, this, config);
52350             this.bindRegion(target, r);
52351         }
52352         return this.regions[target];
52353     },
52354
52355     // private (kinda)
52356     bindRegion : function(name, r){
52357         this.regions[name] = r;
52358         r.on("visibilitychange", this.layout, this);
52359         r.on("paneladded", this.layout, this);
52360         r.on("panelremoved", this.layout, this);
52361         r.on("invalidated", this.layout, this);
52362         r.on("resized", this.onRegionResized, this);
52363         r.on("collapsed", this.onRegionCollapsed, this);
52364         r.on("expanded", this.onRegionExpanded, this);
52365     },
52366
52367     /**
52368      * Performs a layout update.
52369      */
52370     layout : function(){
52371         if(this.updating) {
52372             return;
52373         }
52374         var size = this.getViewSize();
52375         var w = size.width;
52376         var h = size.height;
52377         var centerW = w;
52378         var centerH = h;
52379         var centerY = 0;
52380         var centerX = 0;
52381         //var x = 0, y = 0;
52382
52383         var rs = this.regions;
52384         var north = rs["north"];
52385         var south = rs["south"]; 
52386         var west = rs["west"];
52387         var east = rs["east"];
52388         var center = rs["center"];
52389         //if(this.hideOnLayout){ // not supported anymore
52390             //c.el.setStyle("display", "none");
52391         //}
52392         if(north && north.isVisible()){
52393             var b = north.getBox();
52394             var m = north.getMargins();
52395             b.width = w - (m.left+m.right);
52396             b.x = m.left;
52397             b.y = m.top;
52398             centerY = b.height + b.y + m.bottom;
52399             centerH -= centerY;
52400             north.updateBox(this.safeBox(b));
52401         }
52402         if(south && south.isVisible()){
52403             var b = south.getBox();
52404             var m = south.getMargins();
52405             b.width = w - (m.left+m.right);
52406             b.x = m.left;
52407             var totalHeight = (b.height + m.top + m.bottom);
52408             b.y = h - totalHeight + m.top;
52409             centerH -= totalHeight;
52410             south.updateBox(this.safeBox(b));
52411         }
52412         if(west && west.isVisible()){
52413             var b = west.getBox();
52414             var m = west.getMargins();
52415             b.height = centerH - (m.top+m.bottom);
52416             b.x = m.left;
52417             b.y = centerY + m.top;
52418             var totalWidth = (b.width + m.left + m.right);
52419             centerX += totalWidth;
52420             centerW -= totalWidth;
52421             west.updateBox(this.safeBox(b));
52422         }
52423         if(east && east.isVisible()){
52424             var b = east.getBox();
52425             var m = east.getMargins();
52426             b.height = centerH - (m.top+m.bottom);
52427             var totalWidth = (b.width + m.left + m.right);
52428             b.x = w - totalWidth + m.left;
52429             b.y = centerY + m.top;
52430             centerW -= totalWidth;
52431             east.updateBox(this.safeBox(b));
52432         }
52433         if(center){
52434             var m = center.getMargins();
52435             var centerBox = {
52436                 x: centerX + m.left,
52437                 y: centerY + m.top,
52438                 width: centerW - (m.left+m.right),
52439                 height: centerH - (m.top+m.bottom)
52440             };
52441             //if(this.hideOnLayout){
52442                 //center.el.setStyle("display", "block");
52443             //}
52444             center.updateBox(this.safeBox(centerBox));
52445         }
52446         this.el.repaint();
52447         this.fireEvent("layout", this);
52448     },
52449
52450     // private
52451     safeBox : function(box){
52452         box.width = Math.max(0, box.width);
52453         box.height = Math.max(0, box.height);
52454         return box;
52455     },
52456
52457     /**
52458      * Adds a ContentPanel (or subclass) to this layout.
52459      * @param {String} target The target region key (north, south, east, west or center).
52460      * @param {Roo.ContentPanel} panel The panel to add
52461      * @return {Roo.ContentPanel} The added panel
52462      */
52463     add : function(target, panel){
52464          
52465         target = target.toLowerCase();
52466         return this.regions[target].add(panel);
52467     },
52468
52469     /**
52470      * Remove a ContentPanel (or subclass) to this layout.
52471      * @param {String} target The target region key (north, south, east, west or center).
52472      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52473      * @return {Roo.ContentPanel} The removed panel
52474      */
52475     remove : function(target, panel){
52476         target = target.toLowerCase();
52477         return this.regions[target].remove(panel);
52478     },
52479
52480     /**
52481      * Searches all regions for a panel with the specified id
52482      * @param {String} panelId
52483      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52484      */
52485     findPanel : function(panelId){
52486         var rs = this.regions;
52487         for(var target in rs){
52488             if(typeof rs[target] != "function"){
52489                 var p = rs[target].getPanel(panelId);
52490                 if(p){
52491                     return p;
52492                 }
52493             }
52494         }
52495         return null;
52496     },
52497
52498     /**
52499      * Searches all regions for a panel with the specified id and activates (shows) it.
52500      * @param {String/ContentPanel} panelId The panels id or the panel itself
52501      * @return {Roo.ContentPanel} The shown panel or null
52502      */
52503     showPanel : function(panelId) {
52504       var rs = this.regions;
52505       for(var target in rs){
52506          var r = rs[target];
52507          if(typeof r != "function"){
52508             if(r.hasPanel(panelId)){
52509                return r.showPanel(panelId);
52510             }
52511          }
52512       }
52513       return null;
52514    },
52515
52516    /**
52517      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52518      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52519      */
52520     restoreState : function(provider){
52521         if(!provider){
52522             provider = Roo.state.Manager;
52523         }
52524         var sm = new Roo.LayoutStateManager();
52525         sm.init(this, provider);
52526     },
52527
52528     /**
52529      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52530      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52531      * a valid ContentPanel config object.  Example:
52532      * <pre><code>
52533 // Create the main layout
52534 var layout = new Roo.BorderLayout('main-ct', {
52535     west: {
52536         split:true,
52537         minSize: 175,
52538         titlebar: true
52539     },
52540     center: {
52541         title:'Components'
52542     }
52543 }, 'main-ct');
52544
52545 // Create and add multiple ContentPanels at once via configs
52546 layout.batchAdd({
52547    west: {
52548        id: 'source-files',
52549        autoCreate:true,
52550        title:'Ext Source Files',
52551        autoScroll:true,
52552        fitToFrame:true
52553    },
52554    center : {
52555        el: cview,
52556        autoScroll:true,
52557        fitToFrame:true,
52558        toolbar: tb,
52559        resizeEl:'cbody'
52560    }
52561 });
52562 </code></pre>
52563      * @param {Object} regions An object containing ContentPanel configs by region name
52564      */
52565     batchAdd : function(regions){
52566         this.beginUpdate();
52567         for(var rname in regions){
52568             var lr = this.regions[rname];
52569             if(lr){
52570                 this.addTypedPanels(lr, regions[rname]);
52571             }
52572         }
52573         this.endUpdate();
52574     },
52575
52576     // private
52577     addTypedPanels : function(lr, ps){
52578         if(typeof ps == 'string'){
52579             lr.add(new Roo.ContentPanel(ps));
52580         }
52581         else if(ps instanceof Array){
52582             for(var i =0, len = ps.length; i < len; i++){
52583                 this.addTypedPanels(lr, ps[i]);
52584             }
52585         }
52586         else if(!ps.events){ // raw config?
52587             var el = ps.el;
52588             delete ps.el; // prevent conflict
52589             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52590         }
52591         else {  // panel object assumed!
52592             lr.add(ps);
52593         }
52594     },
52595     /**
52596      * Adds a xtype elements to the layout.
52597      * <pre><code>
52598
52599 layout.addxtype({
52600        xtype : 'ContentPanel',
52601        region: 'west',
52602        items: [ .... ]
52603    }
52604 );
52605
52606 layout.addxtype({
52607         xtype : 'NestedLayoutPanel',
52608         region: 'west',
52609         layout: {
52610            center: { },
52611            west: { }   
52612         },
52613         items : [ ... list of content panels or nested layout panels.. ]
52614    }
52615 );
52616 </code></pre>
52617      * @param {Object} cfg Xtype definition of item to add.
52618      */
52619     addxtype : function(cfg)
52620     {
52621         // basically accepts a pannel...
52622         // can accept a layout region..!?!?
52623         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52624         
52625         if (!cfg.xtype.match(/Panel$/)) {
52626             return false;
52627         }
52628         var ret = false;
52629         
52630         if (typeof(cfg.region) == 'undefined') {
52631             Roo.log("Failed to add Panel, region was not set");
52632             Roo.log(cfg);
52633             return false;
52634         }
52635         var region = cfg.region;
52636         delete cfg.region;
52637         
52638           
52639         var xitems = [];
52640         if (cfg.items) {
52641             xitems = cfg.items;
52642             delete cfg.items;
52643         }
52644         var nb = false;
52645         
52646         switch(cfg.xtype) 
52647         {
52648             case 'ContentPanel':  // ContentPanel (el, cfg)
52649             case 'ScrollPanel':  // ContentPanel (el, cfg)
52650             case 'ViewPanel': 
52651                 if(cfg.autoCreate) {
52652                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52653                 } else {
52654                     var el = this.el.createChild();
52655                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52656                 }
52657                 
52658                 this.add(region, ret);
52659                 break;
52660             
52661             
52662             case 'TreePanel': // our new panel!
52663                 cfg.el = this.el.createChild();
52664                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52665                 this.add(region, ret);
52666                 break;
52667             
52668             case 'NestedLayoutPanel': 
52669                 // create a new Layout (which is  a Border Layout...
52670                 var el = this.el.createChild();
52671                 var clayout = cfg.layout;
52672                 delete cfg.layout;
52673                 clayout.items   = clayout.items  || [];
52674                 // replace this exitems with the clayout ones..
52675                 xitems = clayout.items;
52676                  
52677                 
52678                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52679                     cfg.background = false;
52680                 }
52681                 var layout = new Roo.BorderLayout(el, clayout);
52682                 
52683                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52684                 //console.log('adding nested layout panel '  + cfg.toSource());
52685                 this.add(region, ret);
52686                 nb = {}; /// find first...
52687                 break;
52688                 
52689             case 'GridPanel': 
52690             
52691                 // needs grid and region
52692                 
52693                 //var el = this.getRegion(region).el.createChild();
52694                 var el = this.el.createChild();
52695                 // create the grid first...
52696                 
52697                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52698                 delete cfg.grid;
52699                 if (region == 'center' && this.active ) {
52700                     cfg.background = false;
52701                 }
52702                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52703                 
52704                 this.add(region, ret);
52705                 if (cfg.background) {
52706                     ret.on('activate', function(gp) {
52707                         if (!gp.grid.rendered) {
52708                             gp.grid.render();
52709                         }
52710                     });
52711                 } else {
52712                     grid.render();
52713                 }
52714                 break;
52715            
52716            
52717            
52718                 
52719                 
52720                 
52721             default:
52722                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52723                     
52724                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52725                     this.add(region, ret);
52726                 } else {
52727                 
52728                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52729                     return null;
52730                 }
52731                 
52732              // GridPanel (grid, cfg)
52733             
52734         }
52735         this.beginUpdate();
52736         // add children..
52737         var region = '';
52738         var abn = {};
52739         Roo.each(xitems, function(i)  {
52740             region = nb && i.region ? i.region : false;
52741             
52742             var add = ret.addxtype(i);
52743            
52744             if (region) {
52745                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52746                 if (!i.background) {
52747                     abn[region] = nb[region] ;
52748                 }
52749             }
52750             
52751         });
52752         this.endUpdate();
52753
52754         // make the last non-background panel active..
52755         //if (nb) { Roo.log(abn); }
52756         if (nb) {
52757             
52758             for(var r in abn) {
52759                 region = this.getRegion(r);
52760                 if (region) {
52761                     // tried using nb[r], but it does not work..
52762                      
52763                     region.showPanel(abn[r]);
52764                    
52765                 }
52766             }
52767         }
52768         return ret;
52769         
52770     }
52771 });
52772
52773 /**
52774  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52775  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52776  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52777  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52778  * <pre><code>
52779 // shorthand
52780 var CP = Roo.ContentPanel;
52781
52782 var layout = Roo.BorderLayout.create({
52783     north: {
52784         initialSize: 25,
52785         titlebar: false,
52786         panels: [new CP("north", "North")]
52787     },
52788     west: {
52789         split:true,
52790         initialSize: 200,
52791         minSize: 175,
52792         maxSize: 400,
52793         titlebar: true,
52794         collapsible: true,
52795         panels: [new CP("west", {title: "West"})]
52796     },
52797     east: {
52798         split:true,
52799         initialSize: 202,
52800         minSize: 175,
52801         maxSize: 400,
52802         titlebar: true,
52803         collapsible: true,
52804         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52805     },
52806     south: {
52807         split:true,
52808         initialSize: 100,
52809         minSize: 100,
52810         maxSize: 200,
52811         titlebar: true,
52812         collapsible: true,
52813         panels: [new CP("south", {title: "South", closable: true})]
52814     },
52815     center: {
52816         titlebar: true,
52817         autoScroll:true,
52818         resizeTabs: true,
52819         minTabWidth: 50,
52820         preferredTabWidth: 150,
52821         panels: [
52822             new CP("center1", {title: "Close Me", closable: true}),
52823             new CP("center2", {title: "Center Panel", closable: false})
52824         ]
52825     }
52826 }, document.body);
52827
52828 layout.getRegion("center").showPanel("center1");
52829 </code></pre>
52830  * @param config
52831  * @param targetEl
52832  */
52833 Roo.BorderLayout.create = function(config, targetEl){
52834     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52835     layout.beginUpdate();
52836     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52837     for(var j = 0, jlen = regions.length; j < jlen; j++){
52838         var lr = regions[j];
52839         if(layout.regions[lr] && config[lr].panels){
52840             var r = layout.regions[lr];
52841             var ps = config[lr].panels;
52842             layout.addTypedPanels(r, ps);
52843         }
52844     }
52845     layout.endUpdate();
52846     return layout;
52847 };
52848
52849 // private
52850 Roo.BorderLayout.RegionFactory = {
52851     // private
52852     validRegions : ["north","south","east","west","center"],
52853
52854     // private
52855     create : function(target, mgr, config){
52856         target = target.toLowerCase();
52857         if(config.lightweight || config.basic){
52858             return new Roo.BasicLayoutRegion(mgr, config, target);
52859         }
52860         switch(target){
52861             case "north":
52862                 return new Roo.NorthLayoutRegion(mgr, config);
52863             case "south":
52864                 return new Roo.SouthLayoutRegion(mgr, config);
52865             case "east":
52866                 return new Roo.EastLayoutRegion(mgr, config);
52867             case "west":
52868                 return new Roo.WestLayoutRegion(mgr, config);
52869             case "center":
52870                 return new Roo.CenterLayoutRegion(mgr, config);
52871         }
52872         throw 'Layout region "'+target+'" not supported.';
52873     }
52874 };/*
52875  * Based on:
52876  * Ext JS Library 1.1.1
52877  * Copyright(c) 2006-2007, Ext JS, LLC.
52878  *
52879  * Originally Released Under LGPL - original licence link has changed is not relivant.
52880  *
52881  * Fork - LGPL
52882  * <script type="text/javascript">
52883  */
52884  
52885 /**
52886  * @class Roo.BasicLayoutRegion
52887  * @extends Roo.util.Observable
52888  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52889  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52890  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52891  */
52892 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52893     this.mgr = mgr;
52894     this.position  = pos;
52895     this.events = {
52896         /**
52897          * @scope Roo.BasicLayoutRegion
52898          */
52899         
52900         /**
52901          * @event beforeremove
52902          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52903          * @param {Roo.LayoutRegion} this
52904          * @param {Roo.ContentPanel} panel The panel
52905          * @param {Object} e The cancel event object
52906          */
52907         "beforeremove" : true,
52908         /**
52909          * @event invalidated
52910          * Fires when the layout for this region is changed.
52911          * @param {Roo.LayoutRegion} this
52912          */
52913         "invalidated" : true,
52914         /**
52915          * @event visibilitychange
52916          * Fires when this region is shown or hidden 
52917          * @param {Roo.LayoutRegion} this
52918          * @param {Boolean} visibility true or false
52919          */
52920         "visibilitychange" : true,
52921         /**
52922          * @event paneladded
52923          * Fires when a panel is added. 
52924          * @param {Roo.LayoutRegion} this
52925          * @param {Roo.ContentPanel} panel The panel
52926          */
52927         "paneladded" : true,
52928         /**
52929          * @event panelremoved
52930          * Fires when a panel is removed. 
52931          * @param {Roo.LayoutRegion} this
52932          * @param {Roo.ContentPanel} panel The panel
52933          */
52934         "panelremoved" : true,
52935         /**
52936          * @event beforecollapse
52937          * Fires when this region before collapse.
52938          * @param {Roo.LayoutRegion} this
52939          */
52940         "beforecollapse" : true,
52941         /**
52942          * @event collapsed
52943          * Fires when this region is collapsed.
52944          * @param {Roo.LayoutRegion} this
52945          */
52946         "collapsed" : true,
52947         /**
52948          * @event expanded
52949          * Fires when this region is expanded.
52950          * @param {Roo.LayoutRegion} this
52951          */
52952         "expanded" : true,
52953         /**
52954          * @event slideshow
52955          * Fires when this region is slid into view.
52956          * @param {Roo.LayoutRegion} this
52957          */
52958         "slideshow" : true,
52959         /**
52960          * @event slidehide
52961          * Fires when this region slides out of view. 
52962          * @param {Roo.LayoutRegion} this
52963          */
52964         "slidehide" : true,
52965         /**
52966          * @event panelactivated
52967          * Fires when a panel is activated. 
52968          * @param {Roo.LayoutRegion} this
52969          * @param {Roo.ContentPanel} panel The activated panel
52970          */
52971         "panelactivated" : true,
52972         /**
52973          * @event resized
52974          * Fires when the user resizes this region. 
52975          * @param {Roo.LayoutRegion} this
52976          * @param {Number} newSize The new size (width for east/west, height for north/south)
52977          */
52978         "resized" : true
52979     };
52980     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52981     this.panels = new Roo.util.MixedCollection();
52982     this.panels.getKey = this.getPanelId.createDelegate(this);
52983     this.box = null;
52984     this.activePanel = null;
52985     // ensure listeners are added...
52986     
52987     if (config.listeners || config.events) {
52988         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52989             listeners : config.listeners || {},
52990             events : config.events || {}
52991         });
52992     }
52993     
52994     if(skipConfig !== true){
52995         this.applyConfig(config);
52996     }
52997 };
52998
52999 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53000     getPanelId : function(p){
53001         return p.getId();
53002     },
53003     
53004     applyConfig : function(config){
53005         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53006         this.config = config;
53007         
53008     },
53009     
53010     /**
53011      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53012      * the width, for horizontal (north, south) the height.
53013      * @param {Number} newSize The new width or height
53014      */
53015     resizeTo : function(newSize){
53016         var el = this.el ? this.el :
53017                  (this.activePanel ? this.activePanel.getEl() : null);
53018         if(el){
53019             switch(this.position){
53020                 case "east":
53021                 case "west":
53022                     el.setWidth(newSize);
53023                     this.fireEvent("resized", this, newSize);
53024                 break;
53025                 case "north":
53026                 case "south":
53027                     el.setHeight(newSize);
53028                     this.fireEvent("resized", this, newSize);
53029                 break;                
53030             }
53031         }
53032     },
53033     
53034     getBox : function(){
53035         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53036     },
53037     
53038     getMargins : function(){
53039         return this.margins;
53040     },
53041     
53042     updateBox : function(box){
53043         this.box = box;
53044         var el = this.activePanel.getEl();
53045         el.dom.style.left = box.x + "px";
53046         el.dom.style.top = box.y + "px";
53047         this.activePanel.setSize(box.width, box.height);
53048     },
53049     
53050     /**
53051      * Returns the container element for this region.
53052      * @return {Roo.Element}
53053      */
53054     getEl : function(){
53055         return this.activePanel;
53056     },
53057     
53058     /**
53059      * Returns true if this region is currently visible.
53060      * @return {Boolean}
53061      */
53062     isVisible : function(){
53063         return this.activePanel ? true : false;
53064     },
53065     
53066     setActivePanel : function(panel){
53067         panel = this.getPanel(panel);
53068         if(this.activePanel && this.activePanel != panel){
53069             this.activePanel.setActiveState(false);
53070             this.activePanel.getEl().setLeftTop(-10000,-10000);
53071         }
53072         this.activePanel = panel;
53073         panel.setActiveState(true);
53074         if(this.box){
53075             panel.setSize(this.box.width, this.box.height);
53076         }
53077         this.fireEvent("panelactivated", this, panel);
53078         this.fireEvent("invalidated");
53079     },
53080     
53081     /**
53082      * Show the specified panel.
53083      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53084      * @return {Roo.ContentPanel} The shown panel or null
53085      */
53086     showPanel : function(panel){
53087         if(panel = this.getPanel(panel)){
53088             this.setActivePanel(panel);
53089         }
53090         return panel;
53091     },
53092     
53093     /**
53094      * Get the active panel for this region.
53095      * @return {Roo.ContentPanel} The active panel or null
53096      */
53097     getActivePanel : function(){
53098         return this.activePanel;
53099     },
53100     
53101     /**
53102      * Add the passed ContentPanel(s)
53103      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53104      * @return {Roo.ContentPanel} The panel added (if only one was added)
53105      */
53106     add : function(panel){
53107         if(arguments.length > 1){
53108             for(var i = 0, len = arguments.length; i < len; i++) {
53109                 this.add(arguments[i]);
53110             }
53111             return null;
53112         }
53113         if(this.hasPanel(panel)){
53114             this.showPanel(panel);
53115             return panel;
53116         }
53117         var el = panel.getEl();
53118         if(el.dom.parentNode != this.mgr.el.dom){
53119             this.mgr.el.dom.appendChild(el.dom);
53120         }
53121         if(panel.setRegion){
53122             panel.setRegion(this);
53123         }
53124         this.panels.add(panel);
53125         el.setStyle("position", "absolute");
53126         if(!panel.background){
53127             this.setActivePanel(panel);
53128             if(this.config.initialSize && this.panels.getCount()==1){
53129                 this.resizeTo(this.config.initialSize);
53130             }
53131         }
53132         this.fireEvent("paneladded", this, panel);
53133         return panel;
53134     },
53135     
53136     /**
53137      * Returns true if the panel is in this region.
53138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53139      * @return {Boolean}
53140      */
53141     hasPanel : function(panel){
53142         if(typeof panel == "object"){ // must be panel obj
53143             panel = panel.getId();
53144         }
53145         return this.getPanel(panel) ? true : false;
53146     },
53147     
53148     /**
53149      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53150      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53151      * @param {Boolean} preservePanel Overrides the config preservePanel option
53152      * @return {Roo.ContentPanel} The panel that was removed
53153      */
53154     remove : function(panel, preservePanel){
53155         panel = this.getPanel(panel);
53156         if(!panel){
53157             return null;
53158         }
53159         var e = {};
53160         this.fireEvent("beforeremove", this, panel, e);
53161         if(e.cancel === true){
53162             return null;
53163         }
53164         var panelId = panel.getId();
53165         this.panels.removeKey(panelId);
53166         return panel;
53167     },
53168     
53169     /**
53170      * Returns the panel specified or null if it's not in this region.
53171      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53172      * @return {Roo.ContentPanel}
53173      */
53174     getPanel : function(id){
53175         if(typeof id == "object"){ // must be panel obj
53176             return id;
53177         }
53178         return this.panels.get(id);
53179     },
53180     
53181     /**
53182      * Returns this regions position (north/south/east/west/center).
53183      * @return {String} 
53184      */
53185     getPosition: function(){
53186         return this.position;    
53187     }
53188 });/*
53189  * Based on:
53190  * Ext JS Library 1.1.1
53191  * Copyright(c) 2006-2007, Ext JS, LLC.
53192  *
53193  * Originally Released Under LGPL - original licence link has changed is not relivant.
53194  *
53195  * Fork - LGPL
53196  * <script type="text/javascript">
53197  */
53198  
53199 /**
53200  * @class Roo.LayoutRegion
53201  * @extends Roo.BasicLayoutRegion
53202  * This class represents a region in a layout manager.
53203  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53204  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53205  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53206  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53207  * @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})
53208  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53209  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53210  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53211  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53212  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53213  * @cfg {String}    title           The title for the region (overrides panel titles)
53214  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53215  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53216  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53217  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53218  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53219  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53220  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53221  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53222  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53223  * @cfg {Boolean}   showPin         True to show a pin button
53224  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53225  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53226  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53227  * @cfg {Number}    width           For East/West panels
53228  * @cfg {Number}    height          For North/South panels
53229  * @cfg {Boolean}   split           To show the splitter
53230  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53231  */
53232 Roo.LayoutRegion = function(mgr, config, pos){
53233     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53234     var dh = Roo.DomHelper;
53235     /** This region's container element 
53236     * @type Roo.Element */
53237     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53238     /** This region's title element 
53239     * @type Roo.Element */
53240
53241     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53242         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53243         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53244     ]}, true);
53245     this.titleEl.enableDisplayMode();
53246     /** This region's title text element 
53247     * @type HTMLElement */
53248     this.titleTextEl = this.titleEl.dom.firstChild;
53249     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53250     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53251     this.closeBtn.enableDisplayMode();
53252     this.closeBtn.on("click", this.closeClicked, this);
53253     this.closeBtn.hide();
53254
53255     this.createBody(config);
53256     this.visible = true;
53257     this.collapsed = false;
53258
53259     if(config.hideWhenEmpty){
53260         this.hide();
53261         this.on("paneladded", this.validateVisibility, this);
53262         this.on("panelremoved", this.validateVisibility, this);
53263     }
53264     this.applyConfig(config);
53265 };
53266
53267 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53268
53269     createBody : function(){
53270         /** This region's body element 
53271         * @type Roo.Element */
53272         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53273     },
53274
53275     applyConfig : function(c){
53276         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53277             var dh = Roo.DomHelper;
53278             if(c.titlebar !== false){
53279                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53280                 this.collapseBtn.on("click", this.collapse, this);
53281                 this.collapseBtn.enableDisplayMode();
53282
53283                 if(c.showPin === true || this.showPin){
53284                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53285                     this.stickBtn.enableDisplayMode();
53286                     this.stickBtn.on("click", this.expand, this);
53287                     this.stickBtn.hide();
53288                 }
53289             }
53290             /** This region's collapsed element
53291             * @type Roo.Element */
53292             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53293                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53294             ]}, true);
53295             if(c.floatable !== false){
53296                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53297                this.collapsedEl.on("click", this.collapseClick, this);
53298             }
53299
53300             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53301                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53302                    id: "message", unselectable: "on", style:{"float":"left"}});
53303                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53304              }
53305             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53306             this.expandBtn.on("click", this.expand, this);
53307         }
53308         if(this.collapseBtn){
53309             this.collapseBtn.setVisible(c.collapsible == true);
53310         }
53311         this.cmargins = c.cmargins || this.cmargins ||
53312                          (this.position == "west" || this.position == "east" ?
53313                              {top: 0, left: 2, right:2, bottom: 0} :
53314                              {top: 2, left: 0, right:0, bottom: 2});
53315         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53316         this.bottomTabs = c.tabPosition != "top";
53317         this.autoScroll = c.autoScroll || false;
53318         if(this.autoScroll){
53319             this.bodyEl.setStyle("overflow", "auto");
53320         }else{
53321             this.bodyEl.setStyle("overflow", "hidden");
53322         }
53323         //if(c.titlebar !== false){
53324             if((!c.titlebar && !c.title) || c.titlebar === false){
53325                 this.titleEl.hide();
53326             }else{
53327                 this.titleEl.show();
53328                 if(c.title){
53329                     this.titleTextEl.innerHTML = c.title;
53330                 }
53331             }
53332         //}
53333         this.duration = c.duration || .30;
53334         this.slideDuration = c.slideDuration || .45;
53335         this.config = c;
53336         if(c.collapsed){
53337             this.collapse(true);
53338         }
53339         if(c.hidden){
53340             this.hide();
53341         }
53342     },
53343     /**
53344      * Returns true if this region is currently visible.
53345      * @return {Boolean}
53346      */
53347     isVisible : function(){
53348         return this.visible;
53349     },
53350
53351     /**
53352      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53353      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53354      */
53355     setCollapsedTitle : function(title){
53356         title = title || "&#160;";
53357         if(this.collapsedTitleTextEl){
53358             this.collapsedTitleTextEl.innerHTML = title;
53359         }
53360     },
53361
53362     getBox : function(){
53363         var b;
53364         if(!this.collapsed){
53365             b = this.el.getBox(false, true);
53366         }else{
53367             b = this.collapsedEl.getBox(false, true);
53368         }
53369         return b;
53370     },
53371
53372     getMargins : function(){
53373         return this.collapsed ? this.cmargins : this.margins;
53374     },
53375
53376     highlight : function(){
53377         this.el.addClass("x-layout-panel-dragover");
53378     },
53379
53380     unhighlight : function(){
53381         this.el.removeClass("x-layout-panel-dragover");
53382     },
53383
53384     updateBox : function(box){
53385         this.box = box;
53386         if(!this.collapsed){
53387             this.el.dom.style.left = box.x + "px";
53388             this.el.dom.style.top = box.y + "px";
53389             this.updateBody(box.width, box.height);
53390         }else{
53391             this.collapsedEl.dom.style.left = box.x + "px";
53392             this.collapsedEl.dom.style.top = box.y + "px";
53393             this.collapsedEl.setSize(box.width, box.height);
53394         }
53395         if(this.tabs){
53396             this.tabs.autoSizeTabs();
53397         }
53398     },
53399
53400     updateBody : function(w, h){
53401         if(w !== null){
53402             this.el.setWidth(w);
53403             w -= this.el.getBorderWidth("rl");
53404             if(this.config.adjustments){
53405                 w += this.config.adjustments[0];
53406             }
53407         }
53408         if(h !== null){
53409             this.el.setHeight(h);
53410             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53411             h -= this.el.getBorderWidth("tb");
53412             if(this.config.adjustments){
53413                 h += this.config.adjustments[1];
53414             }
53415             this.bodyEl.setHeight(h);
53416             if(this.tabs){
53417                 h = this.tabs.syncHeight(h);
53418             }
53419         }
53420         if(this.panelSize){
53421             w = w !== null ? w : this.panelSize.width;
53422             h = h !== null ? h : this.panelSize.height;
53423         }
53424         if(this.activePanel){
53425             var el = this.activePanel.getEl();
53426             w = w !== null ? w : el.getWidth();
53427             h = h !== null ? h : el.getHeight();
53428             this.panelSize = {width: w, height: h};
53429             this.activePanel.setSize(w, h);
53430         }
53431         if(Roo.isIE && this.tabs){
53432             this.tabs.el.repaint();
53433         }
53434     },
53435
53436     /**
53437      * Returns the container element for this region.
53438      * @return {Roo.Element}
53439      */
53440     getEl : function(){
53441         return this.el;
53442     },
53443
53444     /**
53445      * Hides this region.
53446      */
53447     hide : function(){
53448         if(!this.collapsed){
53449             this.el.dom.style.left = "-2000px";
53450             this.el.hide();
53451         }else{
53452             this.collapsedEl.dom.style.left = "-2000px";
53453             this.collapsedEl.hide();
53454         }
53455         this.visible = false;
53456         this.fireEvent("visibilitychange", this, false);
53457     },
53458
53459     /**
53460      * Shows this region if it was previously hidden.
53461      */
53462     show : function(){
53463         if(!this.collapsed){
53464             this.el.show();
53465         }else{
53466             this.collapsedEl.show();
53467         }
53468         this.visible = true;
53469         this.fireEvent("visibilitychange", this, true);
53470     },
53471
53472     closeClicked : function(){
53473         if(this.activePanel){
53474             this.remove(this.activePanel);
53475         }
53476     },
53477
53478     collapseClick : function(e){
53479         if(this.isSlid){
53480            e.stopPropagation();
53481            this.slideIn();
53482         }else{
53483            e.stopPropagation();
53484            this.slideOut();
53485         }
53486     },
53487
53488     /**
53489      * Collapses this region.
53490      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53491      */
53492     collapse : function(skipAnim, skipCheck){
53493         if(this.collapsed) {
53494             return;
53495         }
53496         
53497         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53498             
53499             this.collapsed = true;
53500             if(this.split){
53501                 this.split.el.hide();
53502             }
53503             if(this.config.animate && skipAnim !== true){
53504                 this.fireEvent("invalidated", this);
53505                 this.animateCollapse();
53506             }else{
53507                 this.el.setLocation(-20000,-20000);
53508                 this.el.hide();
53509                 this.collapsedEl.show();
53510                 this.fireEvent("collapsed", this);
53511                 this.fireEvent("invalidated", this);
53512             }
53513         }
53514         
53515     },
53516
53517     animateCollapse : function(){
53518         // overridden
53519     },
53520
53521     /**
53522      * Expands this region if it was previously collapsed.
53523      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53524      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53525      */
53526     expand : function(e, skipAnim){
53527         if(e) {
53528             e.stopPropagation();
53529         }
53530         if(!this.collapsed || this.el.hasActiveFx()) {
53531             return;
53532         }
53533         if(this.isSlid){
53534             this.afterSlideIn();
53535             skipAnim = true;
53536         }
53537         this.collapsed = false;
53538         if(this.config.animate && skipAnim !== true){
53539             this.animateExpand();
53540         }else{
53541             this.el.show();
53542             if(this.split){
53543                 this.split.el.show();
53544             }
53545             this.collapsedEl.setLocation(-2000,-2000);
53546             this.collapsedEl.hide();
53547             this.fireEvent("invalidated", this);
53548             this.fireEvent("expanded", this);
53549         }
53550     },
53551
53552     animateExpand : function(){
53553         // overridden
53554     },
53555
53556     initTabs : function()
53557     {
53558         this.bodyEl.setStyle("overflow", "hidden");
53559         var ts = new Roo.TabPanel(
53560                 this.bodyEl.dom,
53561                 {
53562                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53563                     disableTooltips: this.config.disableTabTips,
53564                     toolbar : this.config.toolbar
53565                 }
53566         );
53567         if(this.config.hideTabs){
53568             ts.stripWrap.setDisplayed(false);
53569         }
53570         this.tabs = ts;
53571         ts.resizeTabs = this.config.resizeTabs === true;
53572         ts.minTabWidth = this.config.minTabWidth || 40;
53573         ts.maxTabWidth = this.config.maxTabWidth || 250;
53574         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53575         ts.monitorResize = false;
53576         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53577         ts.bodyEl.addClass('x-layout-tabs-body');
53578         this.panels.each(this.initPanelAsTab, this);
53579     },
53580
53581     initPanelAsTab : function(panel){
53582         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53583                     this.config.closeOnTab && panel.isClosable());
53584         if(panel.tabTip !== undefined){
53585             ti.setTooltip(panel.tabTip);
53586         }
53587         ti.on("activate", function(){
53588               this.setActivePanel(panel);
53589         }, this);
53590         if(this.config.closeOnTab){
53591             ti.on("beforeclose", function(t, e){
53592                 e.cancel = true;
53593                 this.remove(panel);
53594             }, this);
53595         }
53596         return ti;
53597     },
53598
53599     updatePanelTitle : function(panel, title){
53600         if(this.activePanel == panel){
53601             this.updateTitle(title);
53602         }
53603         if(this.tabs){
53604             var ti = this.tabs.getTab(panel.getEl().id);
53605             ti.setText(title);
53606             if(panel.tabTip !== undefined){
53607                 ti.setTooltip(panel.tabTip);
53608             }
53609         }
53610     },
53611
53612     updateTitle : function(title){
53613         if(this.titleTextEl && !this.config.title){
53614             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53615         }
53616     },
53617
53618     setActivePanel : function(panel){
53619         panel = this.getPanel(panel);
53620         if(this.activePanel && this.activePanel != panel){
53621             this.activePanel.setActiveState(false);
53622         }
53623         this.activePanel = panel;
53624         panel.setActiveState(true);
53625         if(this.panelSize){
53626             panel.setSize(this.panelSize.width, this.panelSize.height);
53627         }
53628         if(this.closeBtn){
53629             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53630         }
53631         this.updateTitle(panel.getTitle());
53632         if(this.tabs){
53633             this.fireEvent("invalidated", this);
53634         }
53635         this.fireEvent("panelactivated", this, panel);
53636     },
53637
53638     /**
53639      * Shows the specified panel.
53640      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53641      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53642      */
53643     showPanel : function(panel)
53644     {
53645         panel = this.getPanel(panel);
53646         if(panel){
53647             if(this.tabs){
53648                 var tab = this.tabs.getTab(panel.getEl().id);
53649                 if(tab.isHidden()){
53650                     this.tabs.unhideTab(tab.id);
53651                 }
53652                 tab.activate();
53653             }else{
53654                 this.setActivePanel(panel);
53655             }
53656         }
53657         return panel;
53658     },
53659
53660     /**
53661      * Get the active panel for this region.
53662      * @return {Roo.ContentPanel} The active panel or null
53663      */
53664     getActivePanel : function(){
53665         return this.activePanel;
53666     },
53667
53668     validateVisibility : function(){
53669         if(this.panels.getCount() < 1){
53670             this.updateTitle("&#160;");
53671             this.closeBtn.hide();
53672             this.hide();
53673         }else{
53674             if(!this.isVisible()){
53675                 this.show();
53676             }
53677         }
53678     },
53679
53680     /**
53681      * Adds the passed ContentPanel(s) to this region.
53682      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53683      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53684      */
53685     add : function(panel){
53686         if(arguments.length > 1){
53687             for(var i = 0, len = arguments.length; i < len; i++) {
53688                 this.add(arguments[i]);
53689             }
53690             return null;
53691         }
53692         if(this.hasPanel(panel)){
53693             this.showPanel(panel);
53694             return panel;
53695         }
53696         panel.setRegion(this);
53697         this.panels.add(panel);
53698         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53699             this.bodyEl.dom.appendChild(panel.getEl().dom);
53700             if(panel.background !== true){
53701                 this.setActivePanel(panel);
53702             }
53703             this.fireEvent("paneladded", this, panel);
53704             return panel;
53705         }
53706         if(!this.tabs){
53707             this.initTabs();
53708         }else{
53709             this.initPanelAsTab(panel);
53710         }
53711         if(panel.background !== true){
53712             this.tabs.activate(panel.getEl().id);
53713         }
53714         this.fireEvent("paneladded", this, panel);
53715         return panel;
53716     },
53717
53718     /**
53719      * Hides the tab for the specified panel.
53720      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53721      */
53722     hidePanel : function(panel){
53723         if(this.tabs && (panel = this.getPanel(panel))){
53724             this.tabs.hideTab(panel.getEl().id);
53725         }
53726     },
53727
53728     /**
53729      * Unhides the tab for a previously hidden panel.
53730      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53731      */
53732     unhidePanel : function(panel){
53733         if(this.tabs && (panel = this.getPanel(panel))){
53734             this.tabs.unhideTab(panel.getEl().id);
53735         }
53736     },
53737
53738     clearPanels : function(){
53739         while(this.panels.getCount() > 0){
53740              this.remove(this.panels.first());
53741         }
53742     },
53743
53744     /**
53745      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53746      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53747      * @param {Boolean} preservePanel Overrides the config preservePanel option
53748      * @return {Roo.ContentPanel} The panel that was removed
53749      */
53750     remove : function(panel, preservePanel){
53751         panel = this.getPanel(panel);
53752         if(!panel){
53753             return null;
53754         }
53755         var e = {};
53756         this.fireEvent("beforeremove", this, panel, e);
53757         if(e.cancel === true){
53758             return null;
53759         }
53760         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53761         var panelId = panel.getId();
53762         this.panels.removeKey(panelId);
53763         if(preservePanel){
53764             document.body.appendChild(panel.getEl().dom);
53765         }
53766         if(this.tabs){
53767             this.tabs.removeTab(panel.getEl().id);
53768         }else if (!preservePanel){
53769             this.bodyEl.dom.removeChild(panel.getEl().dom);
53770         }
53771         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53772             var p = this.panels.first();
53773             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53774             tempEl.appendChild(p.getEl().dom);
53775             this.bodyEl.update("");
53776             this.bodyEl.dom.appendChild(p.getEl().dom);
53777             tempEl = null;
53778             this.updateTitle(p.getTitle());
53779             this.tabs = null;
53780             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53781             this.setActivePanel(p);
53782         }
53783         panel.setRegion(null);
53784         if(this.activePanel == panel){
53785             this.activePanel = null;
53786         }
53787         if(this.config.autoDestroy !== false && preservePanel !== true){
53788             try{panel.destroy();}catch(e){}
53789         }
53790         this.fireEvent("panelremoved", this, panel);
53791         return panel;
53792     },
53793
53794     /**
53795      * Returns the TabPanel component used by this region
53796      * @return {Roo.TabPanel}
53797      */
53798     getTabs : function(){
53799         return this.tabs;
53800     },
53801
53802     createTool : function(parentEl, className){
53803         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53804             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53805         btn.addClassOnOver("x-layout-tools-button-over");
53806         return btn;
53807     }
53808 });/*
53809  * Based on:
53810  * Ext JS Library 1.1.1
53811  * Copyright(c) 2006-2007, Ext JS, LLC.
53812  *
53813  * Originally Released Under LGPL - original licence link has changed is not relivant.
53814  *
53815  * Fork - LGPL
53816  * <script type="text/javascript">
53817  */
53818  
53819
53820
53821 /**
53822  * @class Roo.SplitLayoutRegion
53823  * @extends Roo.LayoutRegion
53824  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53825  */
53826 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53827     this.cursor = cursor;
53828     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53829 };
53830
53831 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53832     splitTip : "Drag to resize.",
53833     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53834     useSplitTips : false,
53835
53836     applyConfig : function(config){
53837         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53838         if(config.split){
53839             if(!this.split){
53840                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53841                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53842                 /** The SplitBar for this region 
53843                 * @type Roo.SplitBar */
53844                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53845                 this.split.on("moved", this.onSplitMove, this);
53846                 this.split.useShim = config.useShim === true;
53847                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53848                 if(this.useSplitTips){
53849                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53850                 }
53851                 if(config.collapsible){
53852                     this.split.el.on("dblclick", this.collapse,  this);
53853                 }
53854             }
53855             if(typeof config.minSize != "undefined"){
53856                 this.split.minSize = config.minSize;
53857             }
53858             if(typeof config.maxSize != "undefined"){
53859                 this.split.maxSize = config.maxSize;
53860             }
53861             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53862                 this.hideSplitter();
53863             }
53864         }
53865     },
53866
53867     getHMaxSize : function(){
53868          var cmax = this.config.maxSize || 10000;
53869          var center = this.mgr.getRegion("center");
53870          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53871     },
53872
53873     getVMaxSize : function(){
53874          var cmax = this.config.maxSize || 10000;
53875          var center = this.mgr.getRegion("center");
53876          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53877     },
53878
53879     onSplitMove : function(split, newSize){
53880         this.fireEvent("resized", this, newSize);
53881     },
53882     
53883     /** 
53884      * Returns the {@link Roo.SplitBar} for this region.
53885      * @return {Roo.SplitBar}
53886      */
53887     getSplitBar : function(){
53888         return this.split;
53889     },
53890     
53891     hide : function(){
53892         this.hideSplitter();
53893         Roo.SplitLayoutRegion.superclass.hide.call(this);
53894     },
53895
53896     hideSplitter : function(){
53897         if(this.split){
53898             this.split.el.setLocation(-2000,-2000);
53899             this.split.el.hide();
53900         }
53901     },
53902
53903     show : function(){
53904         if(this.split){
53905             this.split.el.show();
53906         }
53907         Roo.SplitLayoutRegion.superclass.show.call(this);
53908     },
53909     
53910     beforeSlide: function(){
53911         if(Roo.isGecko){// firefox overflow auto bug workaround
53912             this.bodyEl.clip();
53913             if(this.tabs) {
53914                 this.tabs.bodyEl.clip();
53915             }
53916             if(this.activePanel){
53917                 this.activePanel.getEl().clip();
53918                 
53919                 if(this.activePanel.beforeSlide){
53920                     this.activePanel.beforeSlide();
53921                 }
53922             }
53923         }
53924     },
53925     
53926     afterSlide : function(){
53927         if(Roo.isGecko){// firefox overflow auto bug workaround
53928             this.bodyEl.unclip();
53929             if(this.tabs) {
53930                 this.tabs.bodyEl.unclip();
53931             }
53932             if(this.activePanel){
53933                 this.activePanel.getEl().unclip();
53934                 if(this.activePanel.afterSlide){
53935                     this.activePanel.afterSlide();
53936                 }
53937             }
53938         }
53939     },
53940
53941     initAutoHide : function(){
53942         if(this.autoHide !== false){
53943             if(!this.autoHideHd){
53944                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53945                 this.autoHideHd = {
53946                     "mouseout": function(e){
53947                         if(!e.within(this.el, true)){
53948                             st.delay(500);
53949                         }
53950                     },
53951                     "mouseover" : function(e){
53952                         st.cancel();
53953                     },
53954                     scope : this
53955                 };
53956             }
53957             this.el.on(this.autoHideHd);
53958         }
53959     },
53960
53961     clearAutoHide : function(){
53962         if(this.autoHide !== false){
53963             this.el.un("mouseout", this.autoHideHd.mouseout);
53964             this.el.un("mouseover", this.autoHideHd.mouseover);
53965         }
53966     },
53967
53968     clearMonitor : function(){
53969         Roo.get(document).un("click", this.slideInIf, this);
53970     },
53971
53972     // these names are backwards but not changed for compat
53973     slideOut : function(){
53974         if(this.isSlid || this.el.hasActiveFx()){
53975             return;
53976         }
53977         this.isSlid = true;
53978         if(this.collapseBtn){
53979             this.collapseBtn.hide();
53980         }
53981         this.closeBtnState = this.closeBtn.getStyle('display');
53982         this.closeBtn.hide();
53983         if(this.stickBtn){
53984             this.stickBtn.show();
53985         }
53986         this.el.show();
53987         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53988         this.beforeSlide();
53989         this.el.setStyle("z-index", 10001);
53990         this.el.slideIn(this.getSlideAnchor(), {
53991             callback: function(){
53992                 this.afterSlide();
53993                 this.initAutoHide();
53994                 Roo.get(document).on("click", this.slideInIf, this);
53995                 this.fireEvent("slideshow", this);
53996             },
53997             scope: this,
53998             block: true
53999         });
54000     },
54001
54002     afterSlideIn : function(){
54003         this.clearAutoHide();
54004         this.isSlid = false;
54005         this.clearMonitor();
54006         this.el.setStyle("z-index", "");
54007         if(this.collapseBtn){
54008             this.collapseBtn.show();
54009         }
54010         this.closeBtn.setStyle('display', this.closeBtnState);
54011         if(this.stickBtn){
54012             this.stickBtn.hide();
54013         }
54014         this.fireEvent("slidehide", this);
54015     },
54016
54017     slideIn : function(cb){
54018         if(!this.isSlid || this.el.hasActiveFx()){
54019             Roo.callback(cb);
54020             return;
54021         }
54022         this.isSlid = false;
54023         this.beforeSlide();
54024         this.el.slideOut(this.getSlideAnchor(), {
54025             callback: function(){
54026                 this.el.setLeftTop(-10000, -10000);
54027                 this.afterSlide();
54028                 this.afterSlideIn();
54029                 Roo.callback(cb);
54030             },
54031             scope: this,
54032             block: true
54033         });
54034     },
54035     
54036     slideInIf : function(e){
54037         if(!e.within(this.el)){
54038             this.slideIn();
54039         }
54040     },
54041
54042     animateCollapse : function(){
54043         this.beforeSlide();
54044         this.el.setStyle("z-index", 20000);
54045         var anchor = this.getSlideAnchor();
54046         this.el.slideOut(anchor, {
54047             callback : function(){
54048                 this.el.setStyle("z-index", "");
54049                 this.collapsedEl.slideIn(anchor, {duration:.3});
54050                 this.afterSlide();
54051                 this.el.setLocation(-10000,-10000);
54052                 this.el.hide();
54053                 this.fireEvent("collapsed", this);
54054             },
54055             scope: this,
54056             block: true
54057         });
54058     },
54059
54060     animateExpand : function(){
54061         this.beforeSlide();
54062         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54063         this.el.setStyle("z-index", 20000);
54064         this.collapsedEl.hide({
54065             duration:.1
54066         });
54067         this.el.slideIn(this.getSlideAnchor(), {
54068             callback : function(){
54069                 this.el.setStyle("z-index", "");
54070                 this.afterSlide();
54071                 if(this.split){
54072                     this.split.el.show();
54073                 }
54074                 this.fireEvent("invalidated", this);
54075                 this.fireEvent("expanded", this);
54076             },
54077             scope: this,
54078             block: true
54079         });
54080     },
54081
54082     anchors : {
54083         "west" : "left",
54084         "east" : "right",
54085         "north" : "top",
54086         "south" : "bottom"
54087     },
54088
54089     sanchors : {
54090         "west" : "l",
54091         "east" : "r",
54092         "north" : "t",
54093         "south" : "b"
54094     },
54095
54096     canchors : {
54097         "west" : "tl-tr",
54098         "east" : "tr-tl",
54099         "north" : "tl-bl",
54100         "south" : "bl-tl"
54101     },
54102
54103     getAnchor : function(){
54104         return this.anchors[this.position];
54105     },
54106
54107     getCollapseAnchor : function(){
54108         return this.canchors[this.position];
54109     },
54110
54111     getSlideAnchor : function(){
54112         return this.sanchors[this.position];
54113     },
54114
54115     getAlignAdj : function(){
54116         var cm = this.cmargins;
54117         switch(this.position){
54118             case "west":
54119                 return [0, 0];
54120             break;
54121             case "east":
54122                 return [0, 0];
54123             break;
54124             case "north":
54125                 return [0, 0];
54126             break;
54127             case "south":
54128                 return [0, 0];
54129             break;
54130         }
54131     },
54132
54133     getExpandAdj : function(){
54134         var c = this.collapsedEl, cm = this.cmargins;
54135         switch(this.position){
54136             case "west":
54137                 return [-(cm.right+c.getWidth()+cm.left), 0];
54138             break;
54139             case "east":
54140                 return [cm.right+c.getWidth()+cm.left, 0];
54141             break;
54142             case "north":
54143                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54144             break;
54145             case "south":
54146                 return [0, cm.top+cm.bottom+c.getHeight()];
54147             break;
54148         }
54149     }
54150 });/*
54151  * Based on:
54152  * Ext JS Library 1.1.1
54153  * Copyright(c) 2006-2007, Ext JS, LLC.
54154  *
54155  * Originally Released Under LGPL - original licence link has changed is not relivant.
54156  *
54157  * Fork - LGPL
54158  * <script type="text/javascript">
54159  */
54160 /*
54161  * These classes are private internal classes
54162  */
54163 Roo.CenterLayoutRegion = function(mgr, config){
54164     Roo.LayoutRegion.call(this, mgr, config, "center");
54165     this.visible = true;
54166     this.minWidth = config.minWidth || 20;
54167     this.minHeight = config.minHeight || 20;
54168 };
54169
54170 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54171     hide : function(){
54172         // center panel can't be hidden
54173     },
54174     
54175     show : function(){
54176         // center panel can't be hidden
54177     },
54178     
54179     getMinWidth: function(){
54180         return this.minWidth;
54181     },
54182     
54183     getMinHeight: function(){
54184         return this.minHeight;
54185     }
54186 });
54187
54188
54189 Roo.NorthLayoutRegion = function(mgr, config){
54190     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54191     if(this.split){
54192         this.split.placement = Roo.SplitBar.TOP;
54193         this.split.orientation = Roo.SplitBar.VERTICAL;
54194         this.split.el.addClass("x-layout-split-v");
54195     }
54196     var size = config.initialSize || config.height;
54197     if(typeof size != "undefined"){
54198         this.el.setHeight(size);
54199     }
54200 };
54201 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54202     orientation: Roo.SplitBar.VERTICAL,
54203     getBox : function(){
54204         if(this.collapsed){
54205             return this.collapsedEl.getBox();
54206         }
54207         var box = this.el.getBox();
54208         if(this.split){
54209             box.height += this.split.el.getHeight();
54210         }
54211         return box;
54212     },
54213     
54214     updateBox : function(box){
54215         if(this.split && !this.collapsed){
54216             box.height -= this.split.el.getHeight();
54217             this.split.el.setLeft(box.x);
54218             this.split.el.setTop(box.y+box.height);
54219             this.split.el.setWidth(box.width);
54220         }
54221         if(this.collapsed){
54222             this.updateBody(box.width, null);
54223         }
54224         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54225     }
54226 });
54227
54228 Roo.SouthLayoutRegion = function(mgr, config){
54229     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54230     if(this.split){
54231         this.split.placement = Roo.SplitBar.BOTTOM;
54232         this.split.orientation = Roo.SplitBar.VERTICAL;
54233         this.split.el.addClass("x-layout-split-v");
54234     }
54235     var size = config.initialSize || config.height;
54236     if(typeof size != "undefined"){
54237         this.el.setHeight(size);
54238     }
54239 };
54240 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54241     orientation: Roo.SplitBar.VERTICAL,
54242     getBox : function(){
54243         if(this.collapsed){
54244             return this.collapsedEl.getBox();
54245         }
54246         var box = this.el.getBox();
54247         if(this.split){
54248             var sh = this.split.el.getHeight();
54249             box.height += sh;
54250             box.y -= sh;
54251         }
54252         return box;
54253     },
54254     
54255     updateBox : function(box){
54256         if(this.split && !this.collapsed){
54257             var sh = this.split.el.getHeight();
54258             box.height -= sh;
54259             box.y += sh;
54260             this.split.el.setLeft(box.x);
54261             this.split.el.setTop(box.y-sh);
54262             this.split.el.setWidth(box.width);
54263         }
54264         if(this.collapsed){
54265             this.updateBody(box.width, null);
54266         }
54267         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54268     }
54269 });
54270
54271 Roo.EastLayoutRegion = function(mgr, config){
54272     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54273     if(this.split){
54274         this.split.placement = Roo.SplitBar.RIGHT;
54275         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54276         this.split.el.addClass("x-layout-split-h");
54277     }
54278     var size = config.initialSize || config.width;
54279     if(typeof size != "undefined"){
54280         this.el.setWidth(size);
54281     }
54282 };
54283 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54284     orientation: Roo.SplitBar.HORIZONTAL,
54285     getBox : function(){
54286         if(this.collapsed){
54287             return this.collapsedEl.getBox();
54288         }
54289         var box = this.el.getBox();
54290         if(this.split){
54291             var sw = this.split.el.getWidth();
54292             box.width += sw;
54293             box.x -= sw;
54294         }
54295         return box;
54296     },
54297
54298     updateBox : function(box){
54299         if(this.split && !this.collapsed){
54300             var sw = this.split.el.getWidth();
54301             box.width -= sw;
54302             this.split.el.setLeft(box.x);
54303             this.split.el.setTop(box.y);
54304             this.split.el.setHeight(box.height);
54305             box.x += sw;
54306         }
54307         if(this.collapsed){
54308             this.updateBody(null, box.height);
54309         }
54310         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54311     }
54312 });
54313
54314 Roo.WestLayoutRegion = function(mgr, config){
54315     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54316     if(this.split){
54317         this.split.placement = Roo.SplitBar.LEFT;
54318         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54319         this.split.el.addClass("x-layout-split-h");
54320     }
54321     var size = config.initialSize || config.width;
54322     if(typeof size != "undefined"){
54323         this.el.setWidth(size);
54324     }
54325 };
54326 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54327     orientation: Roo.SplitBar.HORIZONTAL,
54328     getBox : function(){
54329         if(this.collapsed){
54330             return this.collapsedEl.getBox();
54331         }
54332         var box = this.el.getBox();
54333         if(this.split){
54334             box.width += this.split.el.getWidth();
54335         }
54336         return box;
54337     },
54338     
54339     updateBox : function(box){
54340         if(this.split && !this.collapsed){
54341             var sw = this.split.el.getWidth();
54342             box.width -= sw;
54343             this.split.el.setLeft(box.x+box.width);
54344             this.split.el.setTop(box.y);
54345             this.split.el.setHeight(box.height);
54346         }
54347         if(this.collapsed){
54348             this.updateBody(null, box.height);
54349         }
54350         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54351     }
54352 });
54353 /*
54354  * Based on:
54355  * Ext JS Library 1.1.1
54356  * Copyright(c) 2006-2007, Ext JS, LLC.
54357  *
54358  * Originally Released Under LGPL - original licence link has changed is not relivant.
54359  *
54360  * Fork - LGPL
54361  * <script type="text/javascript">
54362  */
54363  
54364  
54365 /*
54366  * Private internal class for reading and applying state
54367  */
54368 Roo.LayoutStateManager = function(layout){
54369      // default empty state
54370      this.state = {
54371         north: {},
54372         south: {},
54373         east: {},
54374         west: {}       
54375     };
54376 };
54377
54378 Roo.LayoutStateManager.prototype = {
54379     init : function(layout, provider){
54380         this.provider = provider;
54381         var state = provider.get(layout.id+"-layout-state");
54382         if(state){
54383             var wasUpdating = layout.isUpdating();
54384             if(!wasUpdating){
54385                 layout.beginUpdate();
54386             }
54387             for(var key in state){
54388                 if(typeof state[key] != "function"){
54389                     var rstate = state[key];
54390                     var r = layout.getRegion(key);
54391                     if(r && rstate){
54392                         if(rstate.size){
54393                             r.resizeTo(rstate.size);
54394                         }
54395                         if(rstate.collapsed == true){
54396                             r.collapse(true);
54397                         }else{
54398                             r.expand(null, true);
54399                         }
54400                     }
54401                 }
54402             }
54403             if(!wasUpdating){
54404                 layout.endUpdate();
54405             }
54406             this.state = state; 
54407         }
54408         this.layout = layout;
54409         layout.on("regionresized", this.onRegionResized, this);
54410         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54411         layout.on("regionexpanded", this.onRegionExpanded, this);
54412     },
54413     
54414     storeState : function(){
54415         this.provider.set(this.layout.id+"-layout-state", this.state);
54416     },
54417     
54418     onRegionResized : function(region, newSize){
54419         this.state[region.getPosition()].size = newSize;
54420         this.storeState();
54421     },
54422     
54423     onRegionCollapsed : function(region){
54424         this.state[region.getPosition()].collapsed = true;
54425         this.storeState();
54426     },
54427     
54428     onRegionExpanded : function(region){
54429         this.state[region.getPosition()].collapsed = false;
54430         this.storeState();
54431     }
54432 };/*
54433  * Based on:
54434  * Ext JS Library 1.1.1
54435  * Copyright(c) 2006-2007, Ext JS, LLC.
54436  *
54437  * Originally Released Under LGPL - original licence link has changed is not relivant.
54438  *
54439  * Fork - LGPL
54440  * <script type="text/javascript">
54441  */
54442 /**
54443  * @class Roo.ContentPanel
54444  * @extends Roo.util.Observable
54445  * @children Roo.form.Form Roo.JsonView Roo.View
54446  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
54447  * A basic ContentPanel element.
54448  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54449  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54450  * @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
54451  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54452  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54453  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54454  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
54455  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54456  * @cfg {String} title          The title for this panel
54457  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54458  * @cfg {String} url            Calls {@link #setUrl} with this value
54459  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54460  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54461  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54462  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54463  * @cfg {String}    style  Extra style to add to the content panel
54464  * @cfg {Roo.menu.Menu} menu  popup menu
54465
54466  * @constructor
54467  * Create a new ContentPanel.
54468  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54469  * @param {String/Object} config A string to set only the title or a config object
54470  * @param {String} content (optional) Set the HTML content for this panel
54471  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54472  */
54473 Roo.ContentPanel = function(el, config, content){
54474     
54475      
54476     /*
54477     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54478         config = el;
54479         el = Roo.id();
54480     }
54481     if (config && config.parentLayout) { 
54482         el = config.parentLayout.el.createChild(); 
54483     }
54484     */
54485     if(el.autoCreate){ // xtype is available if this is called from factory
54486         config = el;
54487         el = Roo.id();
54488     }
54489     this.el = Roo.get(el);
54490     if(!this.el && config && config.autoCreate){
54491         if(typeof config.autoCreate == "object"){
54492             if(!config.autoCreate.id){
54493                 config.autoCreate.id = config.id||el;
54494             }
54495             this.el = Roo.DomHelper.append(document.body,
54496                         config.autoCreate, true);
54497         }else{
54498             this.el = Roo.DomHelper.append(document.body,
54499                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54500         }
54501     }
54502     
54503     
54504     this.closable = false;
54505     this.loaded = false;
54506     this.active = false;
54507     if(typeof config == "string"){
54508         this.title = config;
54509     }else{
54510         Roo.apply(this, config);
54511     }
54512     
54513     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54514         this.wrapEl = this.el.wrap();
54515         this.toolbar.container = this.el.insertSibling(false, 'before');
54516         this.toolbar = new Roo.Toolbar(this.toolbar);
54517     }
54518     
54519     // xtype created footer. - not sure if will work as we normally have to render first..
54520     if (this.footer && !this.footer.el && this.footer.xtype) {
54521         if (!this.wrapEl) {
54522             this.wrapEl = this.el.wrap();
54523         }
54524     
54525         this.footer.container = this.wrapEl.createChild();
54526          
54527         this.footer = Roo.factory(this.footer, Roo);
54528         
54529     }
54530     
54531     if(this.resizeEl){
54532         this.resizeEl = Roo.get(this.resizeEl, true);
54533     }else{
54534         this.resizeEl = this.el;
54535     }
54536     // handle view.xtype
54537     
54538  
54539     
54540     
54541     this.addEvents({
54542         /**
54543          * @event activate
54544          * Fires when this panel is activated. 
54545          * @param {Roo.ContentPanel} this
54546          */
54547         "activate" : true,
54548         /**
54549          * @event deactivate
54550          * Fires when this panel is activated. 
54551          * @param {Roo.ContentPanel} this
54552          */
54553         "deactivate" : true,
54554
54555         /**
54556          * @event resize
54557          * Fires when this panel is resized if fitToFrame is true.
54558          * @param {Roo.ContentPanel} this
54559          * @param {Number} width The width after any component adjustments
54560          * @param {Number} height The height after any component adjustments
54561          */
54562         "resize" : true,
54563         
54564          /**
54565          * @event render
54566          * Fires when this tab is created
54567          * @param {Roo.ContentPanel} this
54568          */
54569         "render" : true
54570          
54571         
54572     });
54573     
54574
54575     
54576     
54577     if(this.autoScroll){
54578         this.resizeEl.setStyle("overflow", "auto");
54579     } else {
54580         // fix randome scrolling
54581         this.el.on('scroll', function() {
54582             Roo.log('fix random scolling');
54583             this.scrollTo('top',0); 
54584         });
54585     }
54586     content = content || this.content;
54587     if(content){
54588         this.setContent(content);
54589     }
54590     if(config && config.url){
54591         this.setUrl(this.url, this.params, this.loadOnce);
54592     }
54593     
54594     
54595     
54596     Roo.ContentPanel.superclass.constructor.call(this);
54597     
54598     if (this.view && typeof(this.view.xtype) != 'undefined') {
54599         this.view.el = this.el.appendChild(document.createElement("div"));
54600         this.view = Roo.factory(this.view); 
54601         this.view.render  &&  this.view.render(false, '');  
54602     }
54603     
54604     
54605     this.fireEvent('render', this);
54606 };
54607
54608 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54609     tabTip:'',
54610     setRegion : function(region){
54611         this.region = region;
54612         if(region){
54613            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54614         }else{
54615            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54616         } 
54617     },
54618     
54619     /**
54620      * Returns the toolbar for this Panel if one was configured. 
54621      * @return {Roo.Toolbar} 
54622      */
54623     getToolbar : function(){
54624         return this.toolbar;
54625     },
54626     
54627     setActiveState : function(active){
54628         this.active = active;
54629         if(!active){
54630             this.fireEvent("deactivate", this);
54631         }else{
54632             this.fireEvent("activate", this);
54633         }
54634     },
54635     /**
54636      * Updates this panel's element
54637      * @param {String} content The new content
54638      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54639     */
54640     setContent : function(content, loadScripts){
54641         this.el.update(content, loadScripts);
54642     },
54643
54644     ignoreResize : function(w, h){
54645         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54646             return true;
54647         }else{
54648             this.lastSize = {width: w, height: h};
54649             return false;
54650         }
54651     },
54652     /**
54653      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54654      * @return {Roo.UpdateManager} The UpdateManager
54655      */
54656     getUpdateManager : function(){
54657         return this.el.getUpdateManager();
54658     },
54659      /**
54660      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54661      * @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:
54662 <pre><code>
54663 panel.load({
54664     url: "your-url.php",
54665     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54666     callback: yourFunction,
54667     scope: yourObject, //(optional scope)
54668     discardUrl: false,
54669     nocache: false,
54670     text: "Loading...",
54671     timeout: 30,
54672     scripts: false
54673 });
54674 </code></pre>
54675      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54676      * 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.
54677      * @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}
54678      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54679      * @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.
54680      * @return {Roo.ContentPanel} this
54681      */
54682     load : function(){
54683         var um = this.el.getUpdateManager();
54684         um.update.apply(um, arguments);
54685         return this;
54686     },
54687
54688
54689     /**
54690      * 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.
54691      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54692      * @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)
54693      * @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)
54694      * @return {Roo.UpdateManager} The UpdateManager
54695      */
54696     setUrl : function(url, params, loadOnce){
54697         if(this.refreshDelegate){
54698             this.removeListener("activate", this.refreshDelegate);
54699         }
54700         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54701         this.on("activate", this.refreshDelegate);
54702         return this.el.getUpdateManager();
54703     },
54704     
54705     _handleRefresh : function(url, params, loadOnce){
54706         if(!loadOnce || !this.loaded){
54707             var updater = this.el.getUpdateManager();
54708             updater.update(url, params, this._setLoaded.createDelegate(this));
54709         }
54710     },
54711     
54712     _setLoaded : function(){
54713         this.loaded = true;
54714     }, 
54715     
54716     /**
54717      * Returns this panel's id
54718      * @return {String} 
54719      */
54720     getId : function(){
54721         return this.el.id;
54722     },
54723     
54724     /** 
54725      * Returns this panel's element - used by regiosn to add.
54726      * @return {Roo.Element} 
54727      */
54728     getEl : function(){
54729         return this.wrapEl || this.el;
54730     },
54731     
54732     adjustForComponents : function(width, height)
54733     {
54734         //Roo.log('adjustForComponents ');
54735         if(this.resizeEl != this.el){
54736             width -= this.el.getFrameWidth('lr');
54737             height -= this.el.getFrameWidth('tb');
54738         }
54739         if(this.toolbar){
54740             var te = this.toolbar.getEl();
54741             height -= te.getHeight();
54742             te.setWidth(width);
54743         }
54744         if(this.footer){
54745             var te = this.footer.getEl();
54746             //Roo.log("footer:" + te.getHeight());
54747             
54748             height -= te.getHeight();
54749             te.setWidth(width);
54750         }
54751         
54752         
54753         if(this.adjustments){
54754             width += this.adjustments[0];
54755             height += this.adjustments[1];
54756         }
54757         return {"width": width, "height": height};
54758     },
54759     
54760     setSize : function(width, height){
54761         if(this.fitToFrame && !this.ignoreResize(width, height)){
54762             if(this.fitContainer && this.resizeEl != this.el){
54763                 this.el.setSize(width, height);
54764             }
54765             var size = this.adjustForComponents(width, height);
54766             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54767             this.fireEvent('resize', this, size.width, size.height);
54768         }
54769     },
54770     
54771     /**
54772      * Returns this panel's title
54773      * @return {String} 
54774      */
54775     getTitle : function(){
54776         return this.title;
54777     },
54778     
54779     /**
54780      * Set this panel's title
54781      * @param {String} title
54782      */
54783     setTitle : function(title){
54784         this.title = title;
54785         if(this.region){
54786             this.region.updatePanelTitle(this, title);
54787         }
54788     },
54789     
54790     /**
54791      * Returns true is this panel was configured to be closable
54792      * @return {Boolean} 
54793      */
54794     isClosable : function(){
54795         return this.closable;
54796     },
54797     
54798     beforeSlide : function(){
54799         this.el.clip();
54800         this.resizeEl.clip();
54801     },
54802     
54803     afterSlide : function(){
54804         this.el.unclip();
54805         this.resizeEl.unclip();
54806     },
54807     
54808     /**
54809      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54810      *   Will fail silently if the {@link #setUrl} method has not been called.
54811      *   This does not activate the panel, just updates its content.
54812      */
54813     refresh : function(){
54814         if(this.refreshDelegate){
54815            this.loaded = false;
54816            this.refreshDelegate();
54817         }
54818     },
54819     
54820     /**
54821      * Destroys this panel
54822      */
54823     destroy : function(){
54824         this.el.removeAllListeners();
54825         var tempEl = document.createElement("span");
54826         tempEl.appendChild(this.el.dom);
54827         tempEl.innerHTML = "";
54828         this.el.remove();
54829         this.el = null;
54830     },
54831     
54832     /**
54833      * form - if the content panel contains a form - this is a reference to it.
54834      * @type {Roo.form.Form}
54835      */
54836     form : false,
54837     /**
54838      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54839      *    This contains a reference to it.
54840      * @type {Roo.View}
54841      */
54842     view : false,
54843     
54844       /**
54845      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54846      * <pre><code>
54847
54848 layout.addxtype({
54849        xtype : 'Form',
54850        items: [ .... ]
54851    }
54852 );
54853
54854 </code></pre>
54855      * @param {Object} cfg Xtype definition of item to add.
54856      */
54857     
54858     addxtype : function(cfg) {
54859         // add form..
54860         if (cfg.xtype.match(/^Form$/)) {
54861             
54862             var el;
54863             //if (this.footer) {
54864             //    el = this.footer.container.insertSibling(false, 'before');
54865             //} else {
54866                 el = this.el.createChild();
54867             //}
54868
54869             this.form = new  Roo.form.Form(cfg);
54870             
54871             
54872             if ( this.form.allItems.length) {
54873                 this.form.render(el.dom);
54874             }
54875             return this.form;
54876         }
54877         // should only have one of theses..
54878         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54879             // views.. should not be just added - used named prop 'view''
54880             
54881             cfg.el = this.el.appendChild(document.createElement("div"));
54882             // factory?
54883             
54884             var ret = new Roo.factory(cfg);
54885              
54886              ret.render && ret.render(false, ''); // render blank..
54887             this.view = ret;
54888             return ret;
54889         }
54890         return false;
54891     }
54892 });
54893
54894 /**
54895  * @class Roo.GridPanel
54896  * @extends Roo.ContentPanel
54897  * @constructor
54898  * Create a new GridPanel.
54899  * @param {Roo.grid.Grid} grid The grid for this panel
54900  * @param {String/Object} config A string to set only the panel's title, or a config object
54901  */
54902 Roo.GridPanel = function(grid, config){
54903     
54904   
54905     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54906         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54907         
54908     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54909     
54910     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54911     
54912     if(this.toolbar){
54913         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54914     }
54915     // xtype created footer. - not sure if will work as we normally have to render first..
54916     if (this.footer && !this.footer.el && this.footer.xtype) {
54917         
54918         this.footer.container = this.grid.getView().getFooterPanel(true);
54919         this.footer.dataSource = this.grid.dataSource;
54920         this.footer = Roo.factory(this.footer, Roo);
54921         
54922     }
54923     
54924     grid.monitorWindowResize = false; // turn off autosizing
54925     grid.autoHeight = false;
54926     grid.autoWidth = false;
54927     this.grid = grid;
54928     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54929 };
54930
54931 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54932     getId : function(){
54933         return this.grid.id;
54934     },
54935     
54936     /**
54937      * Returns the grid for this panel
54938      * @return {Roo.grid.Grid} 
54939      */
54940     getGrid : function(){
54941         return this.grid;    
54942     },
54943     
54944     setSize : function(width, height){
54945         if(!this.ignoreResize(width, height)){
54946             var grid = this.grid;
54947             var size = this.adjustForComponents(width, height);
54948             grid.getGridEl().setSize(size.width, size.height);
54949             grid.autoSize();
54950         }
54951     },
54952     
54953     beforeSlide : function(){
54954         this.grid.getView().scroller.clip();
54955     },
54956     
54957     afterSlide : function(){
54958         this.grid.getView().scroller.unclip();
54959     },
54960     
54961     destroy : function(){
54962         this.grid.destroy();
54963         delete this.grid;
54964         Roo.GridPanel.superclass.destroy.call(this); 
54965     }
54966 });
54967
54968
54969 /**
54970  * @class Roo.NestedLayoutPanel
54971  * @extends Roo.ContentPanel
54972  * @constructor
54973  * Create a new NestedLayoutPanel.
54974  * 
54975  * 
54976  * @param {Roo.BorderLayout} layout [required] The layout for this panel
54977  * @param {String/Object} config A string to set only the title or a config object
54978  */
54979 Roo.NestedLayoutPanel = function(layout, config)
54980 {
54981     // construct with only one argument..
54982     /* FIXME - implement nicer consturctors
54983     if (layout.layout) {
54984         config = layout;
54985         layout = config.layout;
54986         delete config.layout;
54987     }
54988     if (layout.xtype && !layout.getEl) {
54989         // then layout needs constructing..
54990         layout = Roo.factory(layout, Roo);
54991     }
54992     */
54993     
54994     
54995     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54996     
54997     layout.monitorWindowResize = false; // turn off autosizing
54998     this.layout = layout;
54999     this.layout.getEl().addClass("x-layout-nested-layout");
55000     
55001     
55002     
55003     
55004 };
55005
55006 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55007
55008     setSize : function(width, height){
55009         if(!this.ignoreResize(width, height)){
55010             var size = this.adjustForComponents(width, height);
55011             var el = this.layout.getEl();
55012             el.setSize(size.width, size.height);
55013             var touch = el.dom.offsetWidth;
55014             this.layout.layout();
55015             // ie requires a double layout on the first pass
55016             if(Roo.isIE && !this.initialized){
55017                 this.initialized = true;
55018                 this.layout.layout();
55019             }
55020         }
55021     },
55022     
55023     // activate all subpanels if not currently active..
55024     
55025     setActiveState : function(active){
55026         this.active = active;
55027         if(!active){
55028             this.fireEvent("deactivate", this);
55029             return;
55030         }
55031         
55032         this.fireEvent("activate", this);
55033         // not sure if this should happen before or after..
55034         if (!this.layout) {
55035             return; // should not happen..
55036         }
55037         var reg = false;
55038         for (var r in this.layout.regions) {
55039             reg = this.layout.getRegion(r);
55040             if (reg.getActivePanel()) {
55041                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55042                 reg.setActivePanel(reg.getActivePanel());
55043                 continue;
55044             }
55045             if (!reg.panels.length) {
55046                 continue;
55047             }
55048             reg.showPanel(reg.getPanel(0));
55049         }
55050         
55051         
55052         
55053         
55054     },
55055     
55056     /**
55057      * Returns the nested BorderLayout for this panel
55058      * @return {Roo.BorderLayout} 
55059      */
55060     getLayout : function(){
55061         return this.layout;
55062     },
55063     
55064      /**
55065      * Adds a xtype elements to the layout of the nested panel
55066      * <pre><code>
55067
55068 panel.addxtype({
55069        xtype : 'ContentPanel',
55070        region: 'west',
55071        items: [ .... ]
55072    }
55073 );
55074
55075 panel.addxtype({
55076         xtype : 'NestedLayoutPanel',
55077         region: 'west',
55078         layout: {
55079            center: { },
55080            west: { }   
55081         },
55082         items : [ ... list of content panels or nested layout panels.. ]
55083    }
55084 );
55085 </code></pre>
55086      * @param {Object} cfg Xtype definition of item to add.
55087      */
55088     addxtype : function(cfg) {
55089         return this.layout.addxtype(cfg);
55090     
55091     }
55092 });
55093
55094 Roo.ScrollPanel = function(el, config, content){
55095     config = config || {};
55096     config.fitToFrame = true;
55097     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55098     
55099     this.el.dom.style.overflow = "hidden";
55100     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55101     this.el.removeClass("x-layout-inactive-content");
55102     this.el.on("mousewheel", this.onWheel, this);
55103
55104     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55105     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55106     up.unselectable(); down.unselectable();
55107     up.on("click", this.scrollUp, this);
55108     down.on("click", this.scrollDown, this);
55109     up.addClassOnOver("x-scroller-btn-over");
55110     down.addClassOnOver("x-scroller-btn-over");
55111     up.addClassOnClick("x-scroller-btn-click");
55112     down.addClassOnClick("x-scroller-btn-click");
55113     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55114
55115     this.resizeEl = this.el;
55116     this.el = wrap; this.up = up; this.down = down;
55117 };
55118
55119 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55120     increment : 100,
55121     wheelIncrement : 5,
55122     scrollUp : function(){
55123         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55124     },
55125
55126     scrollDown : function(){
55127         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55128     },
55129
55130     afterScroll : function(){
55131         var el = this.resizeEl;
55132         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55133         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55134         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55135     },
55136
55137     setSize : function(){
55138         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55139         this.afterScroll();
55140     },
55141
55142     onWheel : function(e){
55143         var d = e.getWheelDelta();
55144         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55145         this.afterScroll();
55146         e.stopEvent();
55147     },
55148
55149     setContent : function(content, loadScripts){
55150         this.resizeEl.update(content, loadScripts);
55151     }
55152
55153 });
55154
55155
55156
55157 /**
55158  * @class Roo.TreePanel
55159  * @extends Roo.ContentPanel
55160  * Treepanel component
55161  * 
55162  * @constructor
55163  * Create a new TreePanel. - defaults to fit/scoll contents.
55164  * @param {String/Object} config A string to set only the panel's title, or a config object
55165  */
55166 Roo.TreePanel = function(config){
55167     var el = config.el;
55168     var tree = config.tree;
55169     delete config.tree; 
55170     delete config.el; // hopefull!
55171     
55172     // wrapper for IE7 strict & safari scroll issue
55173     
55174     var treeEl = el.createChild();
55175     config.resizeEl = treeEl;
55176     
55177     
55178     
55179     Roo.TreePanel.superclass.constructor.call(this, el, config);
55180  
55181  
55182     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55183     //console.log(tree);
55184     this.on('activate', function()
55185     {
55186         if (this.tree.rendered) {
55187             return;
55188         }
55189         //console.log('render tree');
55190         this.tree.render();
55191     });
55192     // this should not be needed.. - it's actually the 'el' that resizes?
55193     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55194     
55195     //this.on('resize',  function (cp, w, h) {
55196     //        this.tree.innerCt.setWidth(w);
55197     //        this.tree.innerCt.setHeight(h);
55198     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55199     //});
55200
55201         
55202     
55203 };
55204
55205 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55206     fitToFrame : true,
55207     autoScroll : true,
55208     /*
55209      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55210      */
55211     tree : false
55212
55213 });
55214
55215
55216
55217
55218
55219
55220
55221
55222
55223
55224
55225 /*
55226  * Based on:
55227  * Ext JS Library 1.1.1
55228  * Copyright(c) 2006-2007, Ext JS, LLC.
55229  *
55230  * Originally Released Under LGPL - original licence link has changed is not relivant.
55231  *
55232  * Fork - LGPL
55233  * <script type="text/javascript">
55234  */
55235  
55236
55237 /**
55238  * @class Roo.ReaderLayout
55239  * @extends Roo.BorderLayout
55240  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55241  * center region containing two nested regions (a top one for a list view and one for item preview below),
55242  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55243  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55244  * expedites the setup of the overall layout and regions for this common application style.
55245  * Example:
55246  <pre><code>
55247 var reader = new Roo.ReaderLayout();
55248 var CP = Roo.ContentPanel;  // shortcut for adding
55249
55250 reader.beginUpdate();
55251 reader.add("north", new CP("north", "North"));
55252 reader.add("west", new CP("west", {title: "West"}));
55253 reader.add("east", new CP("east", {title: "East"}));
55254
55255 reader.regions.listView.add(new CP("listView", "List"));
55256 reader.regions.preview.add(new CP("preview", "Preview"));
55257 reader.endUpdate();
55258 </code></pre>
55259 * @constructor
55260 * Create a new ReaderLayout
55261 * @param {Object} config Configuration options
55262 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55263 * document.body if omitted)
55264 */
55265 Roo.ReaderLayout = function(config, renderTo){
55266     var c = config || {size:{}};
55267     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55268         north: c.north !== false ? Roo.apply({
55269             split:false,
55270             initialSize: 32,
55271             titlebar: false
55272         }, c.north) : false,
55273         west: c.west !== false ? Roo.apply({
55274             split:true,
55275             initialSize: 200,
55276             minSize: 175,
55277             maxSize: 400,
55278             titlebar: true,
55279             collapsible: true,
55280             animate: true,
55281             margins:{left:5,right:0,bottom:5,top:5},
55282             cmargins:{left:5,right:5,bottom:5,top:5}
55283         }, c.west) : false,
55284         east: c.east !== false ? Roo.apply({
55285             split:true,
55286             initialSize: 200,
55287             minSize: 175,
55288             maxSize: 400,
55289             titlebar: true,
55290             collapsible: true,
55291             animate: true,
55292             margins:{left:0,right:5,bottom:5,top:5},
55293             cmargins:{left:5,right:5,bottom:5,top:5}
55294         }, c.east) : false,
55295         center: Roo.apply({
55296             tabPosition: 'top',
55297             autoScroll:false,
55298             closeOnTab: true,
55299             titlebar:false,
55300             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55301         }, c.center)
55302     });
55303
55304     this.el.addClass('x-reader');
55305
55306     this.beginUpdate();
55307
55308     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55309         south: c.preview !== false ? Roo.apply({
55310             split:true,
55311             initialSize: 200,
55312             minSize: 100,
55313             autoScroll:true,
55314             collapsible:true,
55315             titlebar: true,
55316             cmargins:{top:5,left:0, right:0, bottom:0}
55317         }, c.preview) : false,
55318         center: Roo.apply({
55319             autoScroll:false,
55320             titlebar:false,
55321             minHeight:200
55322         }, c.listView)
55323     });
55324     this.add('center', new Roo.NestedLayoutPanel(inner,
55325             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55326
55327     this.endUpdate();
55328
55329     this.regions.preview = inner.getRegion('south');
55330     this.regions.listView = inner.getRegion('center');
55331 };
55332
55333 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55334  * Based on:
55335  * Ext JS Library 1.1.1
55336  * Copyright(c) 2006-2007, Ext JS, LLC.
55337  *
55338  * Originally Released Under LGPL - original licence link has changed is not relivant.
55339  *
55340  * Fork - LGPL
55341  * <script type="text/javascript">
55342  */
55343  
55344 /**
55345  * @class Roo.grid.Grid
55346  * @extends Roo.util.Observable
55347  * This class represents the primary interface of a component based grid control.
55348  * <br><br>Usage:<pre><code>
55349  var grid = new Roo.grid.Grid("my-container-id", {
55350      ds: myDataStore,
55351      cm: myColModel,
55352      selModel: mySelectionModel,
55353      autoSizeColumns: true,
55354      monitorWindowResize: false,
55355      trackMouseOver: true
55356  });
55357  // set any options
55358  grid.render();
55359  * </code></pre>
55360  * <b>Common Problems:</b><br/>
55361  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55362  * element will correct this<br/>
55363  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55364  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55365  * are unpredictable.<br/>
55366  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55367  * grid to calculate dimensions/offsets.<br/>
55368   * @constructor
55369  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55370  * The container MUST have some type of size defined for the grid to fill. The container will be
55371  * automatically set to position relative if it isn't already.
55372  * @param {Object} config A config object that sets properties on this grid.
55373  */
55374 Roo.grid.Grid = function(container, config){
55375         // initialize the container
55376         this.container = Roo.get(container);
55377         this.container.update("");
55378         this.container.setStyle("overflow", "hidden");
55379     this.container.addClass('x-grid-container');
55380
55381     this.id = this.container.id;
55382
55383     Roo.apply(this, config);
55384     // check and correct shorthanded configs
55385     if(this.ds){
55386         this.dataSource = this.ds;
55387         delete this.ds;
55388     }
55389     if(this.cm){
55390         this.colModel = this.cm;
55391         delete this.cm;
55392     }
55393     if(this.sm){
55394         this.selModel = this.sm;
55395         delete this.sm;
55396     }
55397
55398     if (this.selModel) {
55399         this.selModel = Roo.factory(this.selModel, Roo.grid);
55400         this.sm = this.selModel;
55401         this.sm.xmodule = this.xmodule || false;
55402     }
55403     if (typeof(this.colModel.config) == 'undefined') {
55404         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55405         this.cm = this.colModel;
55406         this.cm.xmodule = this.xmodule || false;
55407     }
55408     if (this.dataSource) {
55409         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55410         this.ds = this.dataSource;
55411         this.ds.xmodule = this.xmodule || false;
55412          
55413     }
55414     
55415     
55416     
55417     if(this.width){
55418         this.container.setWidth(this.width);
55419     }
55420
55421     if(this.height){
55422         this.container.setHeight(this.height);
55423     }
55424     /** @private */
55425         this.addEvents({
55426         // raw events
55427         /**
55428          * @event click
55429          * The raw click event for the entire grid.
55430          * @param {Roo.EventObject} e
55431          */
55432         "click" : true,
55433         /**
55434          * @event dblclick
55435          * The raw dblclick event for the entire grid.
55436          * @param {Roo.EventObject} e
55437          */
55438         "dblclick" : true,
55439         /**
55440          * @event contextmenu
55441          * The raw contextmenu event for the entire grid.
55442          * @param {Roo.EventObject} e
55443          */
55444         "contextmenu" : true,
55445         /**
55446          * @event mousedown
55447          * The raw mousedown event for the entire grid.
55448          * @param {Roo.EventObject} e
55449          */
55450         "mousedown" : true,
55451         /**
55452          * @event mouseup
55453          * The raw mouseup event for the entire grid.
55454          * @param {Roo.EventObject} e
55455          */
55456         "mouseup" : true,
55457         /**
55458          * @event mouseover
55459          * The raw mouseover event for the entire grid.
55460          * @param {Roo.EventObject} e
55461          */
55462         "mouseover" : true,
55463         /**
55464          * @event mouseout
55465          * The raw mouseout event for the entire grid.
55466          * @param {Roo.EventObject} e
55467          */
55468         "mouseout" : true,
55469         /**
55470          * @event keypress
55471          * The raw keypress event for the entire grid.
55472          * @param {Roo.EventObject} e
55473          */
55474         "keypress" : true,
55475         /**
55476          * @event keydown
55477          * The raw keydown event for the entire grid.
55478          * @param {Roo.EventObject} e
55479          */
55480         "keydown" : true,
55481
55482         // custom events
55483
55484         /**
55485          * @event cellclick
55486          * Fires when a cell is clicked
55487          * @param {Grid} this
55488          * @param {Number} rowIndex
55489          * @param {Number} columnIndex
55490          * @param {Roo.EventObject} e
55491          */
55492         "cellclick" : true,
55493         /**
55494          * @event celldblclick
55495          * Fires when a cell is double clicked
55496          * @param {Grid} this
55497          * @param {Number} rowIndex
55498          * @param {Number} columnIndex
55499          * @param {Roo.EventObject} e
55500          */
55501         "celldblclick" : true,
55502         /**
55503          * @event rowclick
55504          * Fires when a row is clicked
55505          * @param {Grid} this
55506          * @param {Number} rowIndex
55507          * @param {Roo.EventObject} e
55508          */
55509         "rowclick" : true,
55510         /**
55511          * @event rowdblclick
55512          * Fires when a row is double clicked
55513          * @param {Grid} this
55514          * @param {Number} rowIndex
55515          * @param {Roo.EventObject} e
55516          */
55517         "rowdblclick" : true,
55518         /**
55519          * @event headerclick
55520          * Fires when a header is clicked
55521          * @param {Grid} this
55522          * @param {Number} columnIndex
55523          * @param {Roo.EventObject} e
55524          */
55525         "headerclick" : true,
55526         /**
55527          * @event headerdblclick
55528          * Fires when a header cell is double clicked
55529          * @param {Grid} this
55530          * @param {Number} columnIndex
55531          * @param {Roo.EventObject} e
55532          */
55533         "headerdblclick" : true,
55534         /**
55535          * @event rowcontextmenu
55536          * Fires when a row is right clicked
55537          * @param {Grid} this
55538          * @param {Number} rowIndex
55539          * @param {Roo.EventObject} e
55540          */
55541         "rowcontextmenu" : true,
55542         /**
55543          * @event cellcontextmenu
55544          * Fires when a cell is right clicked
55545          * @param {Grid} this
55546          * @param {Number} rowIndex
55547          * @param {Number} cellIndex
55548          * @param {Roo.EventObject} e
55549          */
55550          "cellcontextmenu" : true,
55551         /**
55552          * @event headercontextmenu
55553          * Fires when a header is right clicked
55554          * @param {Grid} this
55555          * @param {Number} columnIndex
55556          * @param {Roo.EventObject} e
55557          */
55558         "headercontextmenu" : true,
55559         /**
55560          * @event bodyscroll
55561          * Fires when the body element is scrolled
55562          * @param {Number} scrollLeft
55563          * @param {Number} scrollTop
55564          */
55565         "bodyscroll" : true,
55566         /**
55567          * @event columnresize
55568          * Fires when the user resizes a column
55569          * @param {Number} columnIndex
55570          * @param {Number} newSize
55571          */
55572         "columnresize" : true,
55573         /**
55574          * @event columnmove
55575          * Fires when the user moves a column
55576          * @param {Number} oldIndex
55577          * @param {Number} newIndex
55578          */
55579         "columnmove" : true,
55580         /**
55581          * @event startdrag
55582          * Fires when row(s) start being dragged
55583          * @param {Grid} this
55584          * @param {Roo.GridDD} dd The drag drop object
55585          * @param {event} e The raw browser event
55586          */
55587         "startdrag" : true,
55588         /**
55589          * @event enddrag
55590          * Fires when a drag operation is complete
55591          * @param {Grid} this
55592          * @param {Roo.GridDD} dd The drag drop object
55593          * @param {event} e The raw browser event
55594          */
55595         "enddrag" : true,
55596         /**
55597          * @event dragdrop
55598          * Fires when dragged row(s) are dropped on a valid DD target
55599          * @param {Grid} this
55600          * @param {Roo.GridDD} dd The drag drop object
55601          * @param {String} targetId The target drag drop object
55602          * @param {event} e The raw browser event
55603          */
55604         "dragdrop" : true,
55605         /**
55606          * @event dragover
55607          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55608          * @param {Grid} this
55609          * @param {Roo.GridDD} dd The drag drop object
55610          * @param {String} targetId The target drag drop object
55611          * @param {event} e The raw browser event
55612          */
55613         "dragover" : true,
55614         /**
55615          * @event dragenter
55616          *  Fires when the dragged row(s) first cross another DD target while being dragged
55617          * @param {Grid} this
55618          * @param {Roo.GridDD} dd The drag drop object
55619          * @param {String} targetId The target drag drop object
55620          * @param {event} e The raw browser event
55621          */
55622         "dragenter" : true,
55623         /**
55624          * @event dragout
55625          * Fires when the dragged row(s) leave another DD target while being dragged
55626          * @param {Grid} this
55627          * @param {Roo.GridDD} dd The drag drop object
55628          * @param {String} targetId The target drag drop object
55629          * @param {event} e The raw browser event
55630          */
55631         "dragout" : true,
55632         /**
55633          * @event rowclass
55634          * Fires when a row is rendered, so you can change add a style to it.
55635          * @param {GridView} gridview   The grid view
55636          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55637          */
55638         'rowclass' : true,
55639
55640         /**
55641          * @event render
55642          * Fires when the grid is rendered
55643          * @param {Grid} grid
55644          */
55645         'render' : true
55646     });
55647
55648     Roo.grid.Grid.superclass.constructor.call(this);
55649 };
55650 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55651     
55652     /**
55653          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
55654          */
55655         /**
55656          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
55657          */
55658         /**
55659          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
55660          */
55661         /**
55662          * @cfg {Roo.grid.Store} ds The data store for the grid
55663          */
55664         /**
55665          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
55666          */
55667         /**
55668      * @cfg {String} ddGroup - drag drop group.
55669      */
55670       /**
55671      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55672      */
55673
55674     /**
55675      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55676      */
55677     minColumnWidth : 25,
55678
55679     /**
55680      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55681      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55682      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55683      */
55684     autoSizeColumns : false,
55685
55686     /**
55687      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55688      */
55689     autoSizeHeaders : true,
55690
55691     /**
55692      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55693      */
55694     monitorWindowResize : true,
55695
55696     /**
55697      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55698      * rows measured to get a columns size. Default is 0 (all rows).
55699      */
55700     maxRowsToMeasure : 0,
55701
55702     /**
55703      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55704      */
55705     trackMouseOver : true,
55706
55707     /**
55708     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55709     */
55710       /**
55711     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55712     */
55713     
55714     /**
55715     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55716     */
55717     enableDragDrop : false,
55718     
55719     /**
55720     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55721     */
55722     enableColumnMove : true,
55723     
55724     /**
55725     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55726     */
55727     enableColumnHide : true,
55728     
55729     /**
55730     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55731     */
55732     enableRowHeightSync : false,
55733     
55734     /**
55735     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55736     */
55737     stripeRows : true,
55738     
55739     /**
55740     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55741     */
55742     autoHeight : false,
55743
55744     /**
55745      * @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.
55746      */
55747     autoExpandColumn : false,
55748
55749     /**
55750     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55751     * Default is 50.
55752     */
55753     autoExpandMin : 50,
55754
55755     /**
55756     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55757     */
55758     autoExpandMax : 1000,
55759
55760     /**
55761     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55762     */
55763     view : null,
55764
55765     /**
55766     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55767     */
55768     loadMask : false,
55769     /**
55770     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55771     */
55772     dropTarget: false,
55773     
55774    
55775     
55776     // private
55777     rendered : false,
55778
55779     /**
55780     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55781     * of a fixed width. Default is false.
55782     */
55783     /**
55784     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55785     */
55786     
55787     
55788     /**
55789     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55790     * %0 is replaced with the number of selected rows.
55791     */
55792     ddText : "{0} selected row{1}",
55793     
55794     
55795     /**
55796      * Called once after all setup has been completed and the grid is ready to be rendered.
55797      * @return {Roo.grid.Grid} this
55798      */
55799     render : function()
55800     {
55801         var c = this.container;
55802         // try to detect autoHeight/width mode
55803         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55804             this.autoHeight = true;
55805         }
55806         var view = this.getView();
55807         view.init(this);
55808
55809         c.on("click", this.onClick, this);
55810         c.on("dblclick", this.onDblClick, this);
55811         c.on("contextmenu", this.onContextMenu, this);
55812         c.on("keydown", this.onKeyDown, this);
55813         if (Roo.isTouch) {
55814             c.on("touchstart", this.onTouchStart, this);
55815         }
55816
55817         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55818
55819         this.getSelectionModel().init(this);
55820
55821         view.render();
55822
55823         if(this.loadMask){
55824             this.loadMask = new Roo.LoadMask(this.container,
55825                     Roo.apply({store:this.dataSource}, this.loadMask));
55826         }
55827         
55828         
55829         if (this.toolbar && this.toolbar.xtype) {
55830             this.toolbar.container = this.getView().getHeaderPanel(true);
55831             this.toolbar = new Roo.Toolbar(this.toolbar);
55832         }
55833         if (this.footer && this.footer.xtype) {
55834             this.footer.dataSource = this.getDataSource();
55835             this.footer.container = this.getView().getFooterPanel(true);
55836             this.footer = Roo.factory(this.footer, Roo);
55837         }
55838         if (this.dropTarget && this.dropTarget.xtype) {
55839             delete this.dropTarget.xtype;
55840             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55841         }
55842         
55843         
55844         this.rendered = true;
55845         this.fireEvent('render', this);
55846         return this;
55847     },
55848
55849     /**
55850      * Reconfigures the grid to use a different Store and Column Model.
55851      * The View will be bound to the new objects and refreshed.
55852      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55853      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55854      */
55855     reconfigure : function(dataSource, colModel){
55856         if(this.loadMask){
55857             this.loadMask.destroy();
55858             this.loadMask = new Roo.LoadMask(this.container,
55859                     Roo.apply({store:dataSource}, this.loadMask));
55860         }
55861         this.view.bind(dataSource, colModel);
55862         this.dataSource = dataSource;
55863         this.colModel = colModel;
55864         this.view.refresh(true);
55865     },
55866     /**
55867      * addColumns
55868      * Add's a column, default at the end..
55869      
55870      * @param {int} position to add (default end)
55871      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55872      */
55873     addColumns : function(pos, ar)
55874     {
55875         
55876         for (var i =0;i< ar.length;i++) {
55877             var cfg = ar[i];
55878             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55879             this.cm.lookup[cfg.id] = cfg;
55880         }
55881         
55882         
55883         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55884             pos = this.cm.config.length; //this.cm.config.push(cfg);
55885         } 
55886         pos = Math.max(0,pos);
55887         ar.unshift(0);
55888         ar.unshift(pos);
55889         this.cm.config.splice.apply(this.cm.config, ar);
55890         
55891         
55892         
55893         this.view.generateRules(this.cm);
55894         this.view.refresh(true);
55895         
55896     },
55897     
55898     
55899     
55900     
55901     // private
55902     onKeyDown : function(e){
55903         this.fireEvent("keydown", e);
55904     },
55905
55906     /**
55907      * Destroy this grid.
55908      * @param {Boolean} removeEl True to remove the element
55909      */
55910     destroy : function(removeEl, keepListeners){
55911         if(this.loadMask){
55912             this.loadMask.destroy();
55913         }
55914         var c = this.container;
55915         c.removeAllListeners();
55916         this.view.destroy();
55917         this.colModel.purgeListeners();
55918         if(!keepListeners){
55919             this.purgeListeners();
55920         }
55921         c.update("");
55922         if(removeEl === true){
55923             c.remove();
55924         }
55925     },
55926
55927     // private
55928     processEvent : function(name, e){
55929         // does this fire select???
55930         //Roo.log('grid:processEvent '  + name);
55931         
55932         if (name != 'touchstart' ) {
55933             this.fireEvent(name, e);    
55934         }
55935         
55936         var t = e.getTarget();
55937         var v = this.view;
55938         var header = v.findHeaderIndex(t);
55939         if(header !== false){
55940             var ename = name == 'touchstart' ? 'click' : name;
55941              
55942             this.fireEvent("header" + ename, this, header, e);
55943         }else{
55944             var row = v.findRowIndex(t);
55945             var cell = v.findCellIndex(t);
55946             if (name == 'touchstart') {
55947                 // first touch is always a click.
55948                 // hopefull this happens after selection is updated.?
55949                 name = false;
55950                 
55951                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55952                     var cs = this.selModel.getSelectedCell();
55953                     if (row == cs[0] && cell == cs[1]){
55954                         name = 'dblclick';
55955                     }
55956                 }
55957                 if (typeof(this.selModel.getSelections) != 'undefined') {
55958                     var cs = this.selModel.getSelections();
55959                     var ds = this.dataSource;
55960                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55961                         name = 'dblclick';
55962                     }
55963                 }
55964                 if (!name) {
55965                     return;
55966                 }
55967             }
55968             
55969             
55970             if(row !== false){
55971                 this.fireEvent("row" + name, this, row, e);
55972                 if(cell !== false){
55973                     this.fireEvent("cell" + name, this, row, cell, e);
55974                 }
55975             }
55976         }
55977     },
55978
55979     // private
55980     onClick : function(e){
55981         this.processEvent("click", e);
55982     },
55983    // private
55984     onTouchStart : function(e){
55985         this.processEvent("touchstart", e);
55986     },
55987
55988     // private
55989     onContextMenu : function(e, t){
55990         this.processEvent("contextmenu", e);
55991     },
55992
55993     // private
55994     onDblClick : function(e){
55995         this.processEvent("dblclick", e);
55996     },
55997
55998     // private
55999     walkCells : function(row, col, step, fn, scope){
56000         var cm = this.colModel, clen = cm.getColumnCount();
56001         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56002         if(step < 0){
56003             if(col < 0){
56004                 row--;
56005                 first = false;
56006             }
56007             while(row >= 0){
56008                 if(!first){
56009                     col = clen-1;
56010                 }
56011                 first = false;
56012                 while(col >= 0){
56013                     if(fn.call(scope || this, row, col, cm) === true){
56014                         return [row, col];
56015                     }
56016                     col--;
56017                 }
56018                 row--;
56019             }
56020         } else {
56021             if(col >= clen){
56022                 row++;
56023                 first = false;
56024             }
56025             while(row < rlen){
56026                 if(!first){
56027                     col = 0;
56028                 }
56029                 first = false;
56030                 while(col < clen){
56031                     if(fn.call(scope || this, row, col, cm) === true){
56032                         return [row, col];
56033                     }
56034                     col++;
56035                 }
56036                 row++;
56037             }
56038         }
56039         return null;
56040     },
56041
56042     // private
56043     getSelections : function(){
56044         return this.selModel.getSelections();
56045     },
56046
56047     /**
56048      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56049      * but if manual update is required this method will initiate it.
56050      */
56051     autoSize : function(){
56052         if(this.rendered){
56053             this.view.layout();
56054             if(this.view.adjustForScroll){
56055                 this.view.adjustForScroll();
56056             }
56057         }
56058     },
56059
56060     /**
56061      * Returns the grid's underlying element.
56062      * @return {Element} The element
56063      */
56064     getGridEl : function(){
56065         return this.container;
56066     },
56067
56068     // private for compatibility, overridden by editor grid
56069     stopEditing : function(){},
56070
56071     /**
56072      * Returns the grid's SelectionModel.
56073      * @return {SelectionModel}
56074      */
56075     getSelectionModel : function(){
56076         if(!this.selModel){
56077             this.selModel = new Roo.grid.RowSelectionModel();
56078         }
56079         return this.selModel;
56080     },
56081
56082     /**
56083      * Returns the grid's DataSource.
56084      * @return {DataSource}
56085      */
56086     getDataSource : function(){
56087         return this.dataSource;
56088     },
56089
56090     /**
56091      * Returns the grid's ColumnModel.
56092      * @return {ColumnModel}
56093      */
56094     getColumnModel : function(){
56095         return this.colModel;
56096     },
56097
56098     /**
56099      * Returns the grid's GridView object.
56100      * @return {GridView}
56101      */
56102     getView : function(){
56103         if(!this.view){
56104             this.view = new Roo.grid.GridView(this.viewConfig);
56105             this.relayEvents(this.view, [
56106                 "beforerowremoved", "beforerowsinserted",
56107                 "beforerefresh", "rowremoved",
56108                 "rowsinserted", "rowupdated" ,"refresh"
56109             ]);
56110         }
56111         return this.view;
56112     },
56113     /**
56114      * Called to get grid's drag proxy text, by default returns this.ddText.
56115      * Override this to put something different in the dragged text.
56116      * @return {String}
56117      */
56118     getDragDropText : function(){
56119         var count = this.selModel.getCount();
56120         return String.format(this.ddText, count, count == 1 ? '' : 's');
56121     }
56122 });
56123 /*
56124  * Based on:
56125  * Ext JS Library 1.1.1
56126  * Copyright(c) 2006-2007, Ext JS, LLC.
56127  *
56128  * Originally Released Under LGPL - original licence link has changed is not relivant.
56129  *
56130  * Fork - LGPL
56131  * <script type="text/javascript">
56132  */
56133  /**
56134  * @class Roo.grid.AbstractGridView
56135  * @extends Roo.util.Observable
56136  * @abstract
56137  * Abstract base class for grid Views
56138  * @constructor
56139  */
56140 Roo.grid.AbstractGridView = function(){
56141         this.grid = null;
56142         
56143         this.events = {
56144             "beforerowremoved" : true,
56145             "beforerowsinserted" : true,
56146             "beforerefresh" : true,
56147             "rowremoved" : true,
56148             "rowsinserted" : true,
56149             "rowupdated" : true,
56150             "refresh" : true
56151         };
56152     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56153 };
56154
56155 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56156     rowClass : "x-grid-row",
56157     cellClass : "x-grid-cell",
56158     tdClass : "x-grid-td",
56159     hdClass : "x-grid-hd",
56160     splitClass : "x-grid-hd-split",
56161     
56162     init: function(grid){
56163         this.grid = grid;
56164                 var cid = this.grid.getGridEl().id;
56165         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56166         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56167         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56168         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56169         },
56170         
56171     getColumnRenderers : function(){
56172         var renderers = [];
56173         var cm = this.grid.colModel;
56174         var colCount = cm.getColumnCount();
56175         for(var i = 0; i < colCount; i++){
56176             renderers[i] = cm.getRenderer(i);
56177         }
56178         return renderers;
56179     },
56180     
56181     getColumnIds : function(){
56182         var ids = [];
56183         var cm = this.grid.colModel;
56184         var colCount = cm.getColumnCount();
56185         for(var i = 0; i < colCount; i++){
56186             ids[i] = cm.getColumnId(i);
56187         }
56188         return ids;
56189     },
56190     
56191     getDataIndexes : function(){
56192         if(!this.indexMap){
56193             this.indexMap = this.buildIndexMap();
56194         }
56195         return this.indexMap.colToData;
56196     },
56197     
56198     getColumnIndexByDataIndex : function(dataIndex){
56199         if(!this.indexMap){
56200             this.indexMap = this.buildIndexMap();
56201         }
56202         return this.indexMap.dataToCol[dataIndex];
56203     },
56204     
56205     /**
56206      * Set a css style for a column dynamically. 
56207      * @param {Number} colIndex The index of the column
56208      * @param {String} name The css property name
56209      * @param {String} value The css value
56210      */
56211     setCSSStyle : function(colIndex, name, value){
56212         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56213         Roo.util.CSS.updateRule(selector, name, value);
56214     },
56215     
56216     generateRules : function(cm){
56217         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56218         Roo.util.CSS.removeStyleSheet(rulesId);
56219         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56220             var cid = cm.getColumnId(i);
56221             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56222                          this.tdSelector, cid, " {\n}\n",
56223                          this.hdSelector, cid, " {\n}\n",
56224                          this.splitSelector, cid, " {\n}\n");
56225         }
56226         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56227     }
56228 });/*
56229  * Based on:
56230  * Ext JS Library 1.1.1
56231  * Copyright(c) 2006-2007, Ext JS, LLC.
56232  *
56233  * Originally Released Under LGPL - original licence link has changed is not relivant.
56234  *
56235  * Fork - LGPL
56236  * <script type="text/javascript">
56237  */
56238
56239 // private
56240 // This is a support class used internally by the Grid components
56241 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56242     this.grid = grid;
56243     this.view = grid.getView();
56244     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56245     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56246     if(hd2){
56247         this.setHandleElId(Roo.id(hd));
56248         this.setOuterHandleElId(Roo.id(hd2));
56249     }
56250     this.scroll = false;
56251 };
56252 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56253     maxDragWidth: 120,
56254     getDragData : function(e){
56255         var t = Roo.lib.Event.getTarget(e);
56256         var h = this.view.findHeaderCell(t);
56257         if(h){
56258             return {ddel: h.firstChild, header:h};
56259         }
56260         return false;
56261     },
56262
56263     onInitDrag : function(e){
56264         this.view.headersDisabled = true;
56265         var clone = this.dragData.ddel.cloneNode(true);
56266         clone.id = Roo.id();
56267         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56268         this.proxy.update(clone);
56269         return true;
56270     },
56271
56272     afterValidDrop : function(){
56273         var v = this.view;
56274         setTimeout(function(){
56275             v.headersDisabled = false;
56276         }, 50);
56277     },
56278
56279     afterInvalidDrop : function(){
56280         var v = this.view;
56281         setTimeout(function(){
56282             v.headersDisabled = false;
56283         }, 50);
56284     }
56285 });
56286 /*
56287  * Based on:
56288  * Ext JS Library 1.1.1
56289  * Copyright(c) 2006-2007, Ext JS, LLC.
56290  *
56291  * Originally Released Under LGPL - original licence link has changed is not relivant.
56292  *
56293  * Fork - LGPL
56294  * <script type="text/javascript">
56295  */
56296 // private
56297 // This is a support class used internally by the Grid components
56298 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56299     this.grid = grid;
56300     this.view = grid.getView();
56301     // split the proxies so they don't interfere with mouse events
56302     this.proxyTop = Roo.DomHelper.append(document.body, {
56303         cls:"col-move-top", html:"&#160;"
56304     }, true);
56305     this.proxyBottom = Roo.DomHelper.append(document.body, {
56306         cls:"col-move-bottom", html:"&#160;"
56307     }, true);
56308     this.proxyTop.hide = this.proxyBottom.hide = function(){
56309         this.setLeftTop(-100,-100);
56310         this.setStyle("visibility", "hidden");
56311     };
56312     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56313     // temporarily disabled
56314     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56315     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56316 };
56317 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56318     proxyOffsets : [-4, -9],
56319     fly: Roo.Element.fly,
56320
56321     getTargetFromEvent : function(e){
56322         var t = Roo.lib.Event.getTarget(e);
56323         var cindex = this.view.findCellIndex(t);
56324         if(cindex !== false){
56325             return this.view.getHeaderCell(cindex);
56326         }
56327         return null;
56328     },
56329
56330     nextVisible : function(h){
56331         var v = this.view, cm = this.grid.colModel;
56332         h = h.nextSibling;
56333         while(h){
56334             if(!cm.isHidden(v.getCellIndex(h))){
56335                 return h;
56336             }
56337             h = h.nextSibling;
56338         }
56339         return null;
56340     },
56341
56342     prevVisible : function(h){
56343         var v = this.view, cm = this.grid.colModel;
56344         h = h.prevSibling;
56345         while(h){
56346             if(!cm.isHidden(v.getCellIndex(h))){
56347                 return h;
56348             }
56349             h = h.prevSibling;
56350         }
56351         return null;
56352     },
56353
56354     positionIndicator : function(h, n, e){
56355         var x = Roo.lib.Event.getPageX(e);
56356         var r = Roo.lib.Dom.getRegion(n.firstChild);
56357         var px, pt, py = r.top + this.proxyOffsets[1];
56358         if((r.right - x) <= (r.right-r.left)/2){
56359             px = r.right+this.view.borderWidth;
56360             pt = "after";
56361         }else{
56362             px = r.left;
56363             pt = "before";
56364         }
56365         var oldIndex = this.view.getCellIndex(h);
56366         var newIndex = this.view.getCellIndex(n);
56367
56368         if(this.grid.colModel.isFixed(newIndex)){
56369             return false;
56370         }
56371
56372         var locked = this.grid.colModel.isLocked(newIndex);
56373
56374         if(pt == "after"){
56375             newIndex++;
56376         }
56377         if(oldIndex < newIndex){
56378             newIndex--;
56379         }
56380         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56381             return false;
56382         }
56383         px +=  this.proxyOffsets[0];
56384         this.proxyTop.setLeftTop(px, py);
56385         this.proxyTop.show();
56386         if(!this.bottomOffset){
56387             this.bottomOffset = this.view.mainHd.getHeight();
56388         }
56389         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56390         this.proxyBottom.show();
56391         return pt;
56392     },
56393
56394     onNodeEnter : function(n, dd, e, data){
56395         if(data.header != n){
56396             this.positionIndicator(data.header, n, e);
56397         }
56398     },
56399
56400     onNodeOver : function(n, dd, e, data){
56401         var result = false;
56402         if(data.header != n){
56403             result = this.positionIndicator(data.header, n, e);
56404         }
56405         if(!result){
56406             this.proxyTop.hide();
56407             this.proxyBottom.hide();
56408         }
56409         return result ? this.dropAllowed : this.dropNotAllowed;
56410     },
56411
56412     onNodeOut : function(n, dd, e, data){
56413         this.proxyTop.hide();
56414         this.proxyBottom.hide();
56415     },
56416
56417     onNodeDrop : function(n, dd, e, data){
56418         var h = data.header;
56419         if(h != n){
56420             var cm = this.grid.colModel;
56421             var x = Roo.lib.Event.getPageX(e);
56422             var r = Roo.lib.Dom.getRegion(n.firstChild);
56423             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56424             var oldIndex = this.view.getCellIndex(h);
56425             var newIndex = this.view.getCellIndex(n);
56426             var locked = cm.isLocked(newIndex);
56427             if(pt == "after"){
56428                 newIndex++;
56429             }
56430             if(oldIndex < newIndex){
56431                 newIndex--;
56432             }
56433             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56434                 return false;
56435             }
56436             cm.setLocked(oldIndex, locked, true);
56437             cm.moveColumn(oldIndex, newIndex);
56438             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56439             return true;
56440         }
56441         return false;
56442     }
56443 });
56444 /*
56445  * Based on:
56446  * Ext JS Library 1.1.1
56447  * Copyright(c) 2006-2007, Ext JS, LLC.
56448  *
56449  * Originally Released Under LGPL - original licence link has changed is not relivant.
56450  *
56451  * Fork - LGPL
56452  * <script type="text/javascript">
56453  */
56454   
56455 /**
56456  * @class Roo.grid.GridView
56457  * @extends Roo.util.Observable
56458  *
56459  * @constructor
56460  * @param {Object} config
56461  */
56462 Roo.grid.GridView = function(config){
56463     Roo.grid.GridView.superclass.constructor.call(this);
56464     this.el = null;
56465
56466     Roo.apply(this, config);
56467 };
56468
56469 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56470
56471     unselectable :  'unselectable="on"',
56472     unselectableCls :  'x-unselectable',
56473     
56474     
56475     rowClass : "x-grid-row",
56476
56477     cellClass : "x-grid-col",
56478
56479     tdClass : "x-grid-td",
56480
56481     hdClass : "x-grid-hd",
56482
56483     splitClass : "x-grid-split",
56484
56485     sortClasses : ["sort-asc", "sort-desc"],
56486
56487     enableMoveAnim : false,
56488
56489     hlColor: "C3DAF9",
56490
56491     dh : Roo.DomHelper,
56492
56493     fly : Roo.Element.fly,
56494
56495     css : Roo.util.CSS,
56496
56497     borderWidth: 1,
56498
56499     splitOffset: 3,
56500
56501     scrollIncrement : 22,
56502
56503     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56504
56505     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56506
56507     bind : function(ds, cm){
56508         if(this.ds){
56509             this.ds.un("load", this.onLoad, this);
56510             this.ds.un("datachanged", this.onDataChange, this);
56511             this.ds.un("add", this.onAdd, this);
56512             this.ds.un("remove", this.onRemove, this);
56513             this.ds.un("update", this.onUpdate, this);
56514             this.ds.un("clear", this.onClear, this);
56515         }
56516         if(ds){
56517             ds.on("load", this.onLoad, this);
56518             ds.on("datachanged", this.onDataChange, this);
56519             ds.on("add", this.onAdd, this);
56520             ds.on("remove", this.onRemove, this);
56521             ds.on("update", this.onUpdate, this);
56522             ds.on("clear", this.onClear, this);
56523         }
56524         this.ds = ds;
56525
56526         if(this.cm){
56527             this.cm.un("widthchange", this.onColWidthChange, this);
56528             this.cm.un("headerchange", this.onHeaderChange, this);
56529             this.cm.un("hiddenchange", this.onHiddenChange, this);
56530             this.cm.un("columnmoved", this.onColumnMove, this);
56531             this.cm.un("columnlockchange", this.onColumnLock, this);
56532         }
56533         if(cm){
56534             this.generateRules(cm);
56535             cm.on("widthchange", this.onColWidthChange, this);
56536             cm.on("headerchange", this.onHeaderChange, this);
56537             cm.on("hiddenchange", this.onHiddenChange, this);
56538             cm.on("columnmoved", this.onColumnMove, this);
56539             cm.on("columnlockchange", this.onColumnLock, this);
56540         }
56541         this.cm = cm;
56542     },
56543
56544     init: function(grid){
56545         Roo.grid.GridView.superclass.init.call(this, grid);
56546
56547         this.bind(grid.dataSource, grid.colModel);
56548
56549         grid.on("headerclick", this.handleHeaderClick, this);
56550
56551         if(grid.trackMouseOver){
56552             grid.on("mouseover", this.onRowOver, this);
56553             grid.on("mouseout", this.onRowOut, this);
56554         }
56555         grid.cancelTextSelection = function(){};
56556         this.gridId = grid.id;
56557
56558         var tpls = this.templates || {};
56559
56560         if(!tpls.master){
56561             tpls.master = new Roo.Template(
56562                '<div class="x-grid" hidefocus="true">',
56563                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56564                   '<div class="x-grid-topbar"></div>',
56565                   '<div class="x-grid-scroller"><div></div></div>',
56566                   '<div class="x-grid-locked">',
56567                       '<div class="x-grid-header">{lockedHeader}</div>',
56568                       '<div class="x-grid-body">{lockedBody}</div>',
56569                   "</div>",
56570                   '<div class="x-grid-viewport">',
56571                       '<div class="x-grid-header">{header}</div>',
56572                       '<div class="x-grid-body">{body}</div>',
56573                   "</div>",
56574                   '<div class="x-grid-bottombar"></div>',
56575                  
56576                   '<div class="x-grid-resize-proxy">&#160;</div>',
56577                "</div>"
56578             );
56579             tpls.master.disableformats = true;
56580         }
56581
56582         if(!tpls.header){
56583             tpls.header = new Roo.Template(
56584                '<table border="0" cellspacing="0" cellpadding="0">',
56585                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56586                "</table>{splits}"
56587             );
56588             tpls.header.disableformats = true;
56589         }
56590         tpls.header.compile();
56591
56592         if(!tpls.hcell){
56593             tpls.hcell = new Roo.Template(
56594                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56595                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56596                 "</div></td>"
56597              );
56598              tpls.hcell.disableFormats = true;
56599         }
56600         tpls.hcell.compile();
56601
56602         if(!tpls.hsplit){
56603             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56604                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56605             tpls.hsplit.disableFormats = true;
56606         }
56607         tpls.hsplit.compile();
56608
56609         if(!tpls.body){
56610             tpls.body = new Roo.Template(
56611                '<table border="0" cellspacing="0" cellpadding="0">',
56612                "<tbody>{rows}</tbody>",
56613                "</table>"
56614             );
56615             tpls.body.disableFormats = true;
56616         }
56617         tpls.body.compile();
56618
56619         if(!tpls.row){
56620             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56621             tpls.row.disableFormats = true;
56622         }
56623         tpls.row.compile();
56624
56625         if(!tpls.cell){
56626             tpls.cell = new Roo.Template(
56627                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56628                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56629                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56630                 "</td>"
56631             );
56632             tpls.cell.disableFormats = true;
56633         }
56634         tpls.cell.compile();
56635
56636         this.templates = tpls;
56637     },
56638
56639     // remap these for backwards compat
56640     onColWidthChange : function(){
56641         this.updateColumns.apply(this, arguments);
56642     },
56643     onHeaderChange : function(){
56644         this.updateHeaders.apply(this, arguments);
56645     }, 
56646     onHiddenChange : function(){
56647         this.handleHiddenChange.apply(this, arguments);
56648     },
56649     onColumnMove : function(){
56650         this.handleColumnMove.apply(this, arguments);
56651     },
56652     onColumnLock : function(){
56653         this.handleLockChange.apply(this, arguments);
56654     },
56655
56656     onDataChange : function(){
56657         this.refresh();
56658         this.updateHeaderSortState();
56659     },
56660
56661     onClear : function(){
56662         this.refresh();
56663     },
56664
56665     onUpdate : function(ds, record){
56666         this.refreshRow(record);
56667     },
56668
56669     refreshRow : function(record){
56670         var ds = this.ds, index;
56671         if(typeof record == 'number'){
56672             index = record;
56673             record = ds.getAt(index);
56674         }else{
56675             index = ds.indexOf(record);
56676         }
56677         this.insertRows(ds, index, index, true);
56678         this.onRemove(ds, record, index+1, true);
56679         this.syncRowHeights(index, index);
56680         this.layout();
56681         this.fireEvent("rowupdated", this, index, record);
56682     },
56683
56684     onAdd : function(ds, records, index){
56685         this.insertRows(ds, index, index + (records.length-1));
56686     },
56687
56688     onRemove : function(ds, record, index, isUpdate){
56689         if(isUpdate !== true){
56690             this.fireEvent("beforerowremoved", this, index, record);
56691         }
56692         var bt = this.getBodyTable(), lt = this.getLockedTable();
56693         if(bt.rows[index]){
56694             bt.firstChild.removeChild(bt.rows[index]);
56695         }
56696         if(lt.rows[index]){
56697             lt.firstChild.removeChild(lt.rows[index]);
56698         }
56699         if(isUpdate !== true){
56700             this.stripeRows(index);
56701             this.syncRowHeights(index, index);
56702             this.layout();
56703             this.fireEvent("rowremoved", this, index, record);
56704         }
56705     },
56706
56707     onLoad : function(){
56708         this.scrollToTop();
56709     },
56710
56711     /**
56712      * Scrolls the grid to the top
56713      */
56714     scrollToTop : function(){
56715         if(this.scroller){
56716             this.scroller.dom.scrollTop = 0;
56717             this.syncScroll();
56718         }
56719     },
56720
56721     /**
56722      * Gets a panel in the header of the grid that can be used for toolbars etc.
56723      * After modifying the contents of this panel a call to grid.autoSize() may be
56724      * required to register any changes in size.
56725      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56726      * @return Roo.Element
56727      */
56728     getHeaderPanel : function(doShow){
56729         if(doShow){
56730             this.headerPanel.show();
56731         }
56732         return this.headerPanel;
56733     },
56734
56735     /**
56736      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56737      * After modifying the contents of this panel a call to grid.autoSize() may be
56738      * required to register any changes in size.
56739      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56740      * @return Roo.Element
56741      */
56742     getFooterPanel : function(doShow){
56743         if(doShow){
56744             this.footerPanel.show();
56745         }
56746         return this.footerPanel;
56747     },
56748
56749     initElements : function(){
56750         var E = Roo.Element;
56751         var el = this.grid.getGridEl().dom.firstChild;
56752         var cs = el.childNodes;
56753
56754         this.el = new E(el);
56755         
56756          this.focusEl = new E(el.firstChild);
56757         this.focusEl.swallowEvent("click", true);
56758         
56759         this.headerPanel = new E(cs[1]);
56760         this.headerPanel.enableDisplayMode("block");
56761
56762         this.scroller = new E(cs[2]);
56763         this.scrollSizer = new E(this.scroller.dom.firstChild);
56764
56765         this.lockedWrap = new E(cs[3]);
56766         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56767         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56768
56769         this.mainWrap = new E(cs[4]);
56770         this.mainHd = new E(this.mainWrap.dom.firstChild);
56771         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56772
56773         this.footerPanel = new E(cs[5]);
56774         this.footerPanel.enableDisplayMode("block");
56775
56776         this.resizeProxy = new E(cs[6]);
56777
56778         this.headerSelector = String.format(
56779            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56780            this.lockedHd.id, this.mainHd.id
56781         );
56782
56783         this.splitterSelector = String.format(
56784            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56785            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56786         );
56787     },
56788     idToCssName : function(s)
56789     {
56790         return s.replace(/[^a-z0-9]+/ig, '-');
56791     },
56792
56793     getHeaderCell : function(index){
56794         return Roo.DomQuery.select(this.headerSelector)[index];
56795     },
56796
56797     getHeaderCellMeasure : function(index){
56798         return this.getHeaderCell(index).firstChild;
56799     },
56800
56801     getHeaderCellText : function(index){
56802         return this.getHeaderCell(index).firstChild.firstChild;
56803     },
56804
56805     getLockedTable : function(){
56806         return this.lockedBody.dom.firstChild;
56807     },
56808
56809     getBodyTable : function(){
56810         return this.mainBody.dom.firstChild;
56811     },
56812
56813     getLockedRow : function(index){
56814         return this.getLockedTable().rows[index];
56815     },
56816
56817     getRow : function(index){
56818         return this.getBodyTable().rows[index];
56819     },
56820
56821     getRowComposite : function(index){
56822         if(!this.rowEl){
56823             this.rowEl = new Roo.CompositeElementLite();
56824         }
56825         var els = [], lrow, mrow;
56826         if(lrow = this.getLockedRow(index)){
56827             els.push(lrow);
56828         }
56829         if(mrow = this.getRow(index)){
56830             els.push(mrow);
56831         }
56832         this.rowEl.elements = els;
56833         return this.rowEl;
56834     },
56835     /**
56836      * Gets the 'td' of the cell
56837      * 
56838      * @param {Integer} rowIndex row to select
56839      * @param {Integer} colIndex column to select
56840      * 
56841      * @return {Object} 
56842      */
56843     getCell : function(rowIndex, colIndex){
56844         var locked = this.cm.getLockedCount();
56845         var source;
56846         if(colIndex < locked){
56847             source = this.lockedBody.dom.firstChild;
56848         }else{
56849             source = this.mainBody.dom.firstChild;
56850             colIndex -= locked;
56851         }
56852         return source.rows[rowIndex].childNodes[colIndex];
56853     },
56854
56855     getCellText : function(rowIndex, colIndex){
56856         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56857     },
56858
56859     getCellBox : function(cell){
56860         var b = this.fly(cell).getBox();
56861         if(Roo.isOpera){ // opera fails to report the Y
56862             b.y = cell.offsetTop + this.mainBody.getY();
56863         }
56864         return b;
56865     },
56866
56867     getCellIndex : function(cell){
56868         var id = String(cell.className).match(this.cellRE);
56869         if(id){
56870             return parseInt(id[1], 10);
56871         }
56872         return 0;
56873     },
56874
56875     findHeaderIndex : function(n){
56876         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56877         return r ? this.getCellIndex(r) : false;
56878     },
56879
56880     findHeaderCell : function(n){
56881         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56882         return r ? r : false;
56883     },
56884
56885     findRowIndex : function(n){
56886         if(!n){
56887             return false;
56888         }
56889         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56890         return r ? r.rowIndex : false;
56891     },
56892
56893     findCellIndex : function(node){
56894         var stop = this.el.dom;
56895         while(node && node != stop){
56896             if(this.findRE.test(node.className)){
56897                 return this.getCellIndex(node);
56898             }
56899             node = node.parentNode;
56900         }
56901         return false;
56902     },
56903
56904     getColumnId : function(index){
56905         return this.cm.getColumnId(index);
56906     },
56907
56908     getSplitters : function()
56909     {
56910         if(this.splitterSelector){
56911            return Roo.DomQuery.select(this.splitterSelector);
56912         }else{
56913             return null;
56914       }
56915     },
56916
56917     getSplitter : function(index){
56918         return this.getSplitters()[index];
56919     },
56920
56921     onRowOver : function(e, t){
56922         var row;
56923         if((row = this.findRowIndex(t)) !== false){
56924             this.getRowComposite(row).addClass("x-grid-row-over");
56925         }
56926     },
56927
56928     onRowOut : function(e, t){
56929         var row;
56930         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56931             this.getRowComposite(row).removeClass("x-grid-row-over");
56932         }
56933     },
56934
56935     renderHeaders : function(){
56936         var cm = this.cm;
56937         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56938         var cb = [], lb = [], sb = [], lsb = [], p = {};
56939         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56940             p.cellId = "x-grid-hd-0-" + i;
56941             p.splitId = "x-grid-csplit-0-" + i;
56942             p.id = cm.getColumnId(i);
56943             p.value = cm.getColumnHeader(i) || "";
56944             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56945             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56946             if(!cm.isLocked(i)){
56947                 cb[cb.length] = ct.apply(p);
56948                 sb[sb.length] = st.apply(p);
56949             }else{
56950                 lb[lb.length] = ct.apply(p);
56951                 lsb[lsb.length] = st.apply(p);
56952             }
56953         }
56954         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56955                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56956     },
56957
56958     updateHeaders : function(){
56959         var html = this.renderHeaders();
56960         this.lockedHd.update(html[0]);
56961         this.mainHd.update(html[1]);
56962     },
56963
56964     /**
56965      * Focuses the specified row.
56966      * @param {Number} row The row index
56967      */
56968     focusRow : function(row)
56969     {
56970         //Roo.log('GridView.focusRow');
56971         var x = this.scroller.dom.scrollLeft;
56972         this.focusCell(row, 0, false);
56973         this.scroller.dom.scrollLeft = x;
56974     },
56975
56976     /**
56977      * Focuses the specified cell.
56978      * @param {Number} row The row index
56979      * @param {Number} col The column index
56980      * @param {Boolean} hscroll false to disable horizontal scrolling
56981      */
56982     focusCell : function(row, col, hscroll)
56983     {
56984         //Roo.log('GridView.focusCell');
56985         var el = this.ensureVisible(row, col, hscroll);
56986         this.focusEl.alignTo(el, "tl-tl");
56987         if(Roo.isGecko){
56988             this.focusEl.focus();
56989         }else{
56990             this.focusEl.focus.defer(1, this.focusEl);
56991         }
56992     },
56993
56994     /**
56995      * Scrolls the specified cell into view
56996      * @param {Number} row The row index
56997      * @param {Number} col The column index
56998      * @param {Boolean} hscroll false to disable horizontal scrolling
56999      */
57000     ensureVisible : function(row, col, hscroll)
57001     {
57002         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57003         //return null; //disable for testing.
57004         if(typeof row != "number"){
57005             row = row.rowIndex;
57006         }
57007         if(row < 0 && row >= this.ds.getCount()){
57008             return  null;
57009         }
57010         col = (col !== undefined ? col : 0);
57011         var cm = this.grid.colModel;
57012         while(cm.isHidden(col)){
57013             col++;
57014         }
57015
57016         var el = this.getCell(row, col);
57017         if(!el){
57018             return null;
57019         }
57020         var c = this.scroller.dom;
57021
57022         var ctop = parseInt(el.offsetTop, 10);
57023         var cleft = parseInt(el.offsetLeft, 10);
57024         var cbot = ctop + el.offsetHeight;
57025         var cright = cleft + el.offsetWidth;
57026         
57027         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57028         var stop = parseInt(c.scrollTop, 10);
57029         var sleft = parseInt(c.scrollLeft, 10);
57030         var sbot = stop + ch;
57031         var sright = sleft + c.clientWidth;
57032         /*
57033         Roo.log('GridView.ensureVisible:' +
57034                 ' ctop:' + ctop +
57035                 ' c.clientHeight:' + c.clientHeight +
57036                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57037                 ' stop:' + stop +
57038                 ' cbot:' + cbot +
57039                 ' sbot:' + sbot +
57040                 ' ch:' + ch  
57041                 );
57042         */
57043         if(ctop < stop){
57044             c.scrollTop = ctop;
57045             //Roo.log("set scrolltop to ctop DISABLE?");
57046         }else if(cbot > sbot){
57047             //Roo.log("set scrolltop to cbot-ch");
57048             c.scrollTop = cbot-ch;
57049         }
57050         
57051         if(hscroll !== false){
57052             if(cleft < sleft){
57053                 c.scrollLeft = cleft;
57054             }else if(cright > sright){
57055                 c.scrollLeft = cright-c.clientWidth;
57056             }
57057         }
57058          
57059         return el;
57060     },
57061
57062     updateColumns : function(){
57063         this.grid.stopEditing();
57064         var cm = this.grid.colModel, colIds = this.getColumnIds();
57065         //var totalWidth = cm.getTotalWidth();
57066         var pos = 0;
57067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57068             //if(cm.isHidden(i)) continue;
57069             var w = cm.getColumnWidth(i);
57070             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57071             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57072         }
57073         this.updateSplitters();
57074     },
57075
57076     generateRules : function(cm){
57077         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57078         Roo.util.CSS.removeStyleSheet(rulesId);
57079         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57080             var cid = cm.getColumnId(i);
57081             var align = '';
57082             if(cm.config[i].align){
57083                 align = 'text-align:'+cm.config[i].align+';';
57084             }
57085             var hidden = '';
57086             if(cm.isHidden(i)){
57087                 hidden = 'display:none;';
57088             }
57089             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57090             ruleBuf.push(
57091                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57092                     this.hdSelector, cid, " {\n", align, width, "}\n",
57093                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57094                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57095         }
57096         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57097     },
57098
57099     updateSplitters : function(){
57100         var cm = this.cm, s = this.getSplitters();
57101         if(s){ // splitters not created yet
57102             var pos = 0, locked = true;
57103             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57104                 if(cm.isHidden(i)) {
57105                     continue;
57106                 }
57107                 var w = cm.getColumnWidth(i); // make sure it's a number
57108                 if(!cm.isLocked(i) && locked){
57109                     pos = 0;
57110                     locked = false;
57111                 }
57112                 pos += w;
57113                 s[i].style.left = (pos-this.splitOffset) + "px";
57114             }
57115         }
57116     },
57117
57118     handleHiddenChange : function(colModel, colIndex, hidden){
57119         if(hidden){
57120             this.hideColumn(colIndex);
57121         }else{
57122             this.unhideColumn(colIndex);
57123         }
57124     },
57125
57126     hideColumn : function(colIndex){
57127         var cid = this.getColumnId(colIndex);
57128         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57129         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57130         if(Roo.isSafari){
57131             this.updateHeaders();
57132         }
57133         this.updateSplitters();
57134         this.layout();
57135     },
57136
57137     unhideColumn : function(colIndex){
57138         var cid = this.getColumnId(colIndex);
57139         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57140         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57141
57142         if(Roo.isSafari){
57143             this.updateHeaders();
57144         }
57145         this.updateSplitters();
57146         this.layout();
57147     },
57148
57149     insertRows : function(dm, firstRow, lastRow, isUpdate){
57150         if(firstRow == 0 && lastRow == dm.getCount()-1){
57151             this.refresh();
57152         }else{
57153             if(!isUpdate){
57154                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57155             }
57156             var s = this.getScrollState();
57157             var markup = this.renderRows(firstRow, lastRow);
57158             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57159             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57160             this.restoreScroll(s);
57161             if(!isUpdate){
57162                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57163                 this.syncRowHeights(firstRow, lastRow);
57164                 this.stripeRows(firstRow);
57165                 this.layout();
57166             }
57167         }
57168     },
57169
57170     bufferRows : function(markup, target, index){
57171         var before = null, trows = target.rows, tbody = target.tBodies[0];
57172         if(index < trows.length){
57173             before = trows[index];
57174         }
57175         var b = document.createElement("div");
57176         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57177         var rows = b.firstChild.rows;
57178         for(var i = 0, len = rows.length; i < len; i++){
57179             if(before){
57180                 tbody.insertBefore(rows[0], before);
57181             }else{
57182                 tbody.appendChild(rows[0]);
57183             }
57184         }
57185         b.innerHTML = "";
57186         b = null;
57187     },
57188
57189     deleteRows : function(dm, firstRow, lastRow){
57190         if(dm.getRowCount()<1){
57191             this.fireEvent("beforerefresh", this);
57192             this.mainBody.update("");
57193             this.lockedBody.update("");
57194             this.fireEvent("refresh", this);
57195         }else{
57196             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57197             var bt = this.getBodyTable();
57198             var tbody = bt.firstChild;
57199             var rows = bt.rows;
57200             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57201                 tbody.removeChild(rows[firstRow]);
57202             }
57203             this.stripeRows(firstRow);
57204             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57205         }
57206     },
57207
57208     updateRows : function(dataSource, firstRow, lastRow){
57209         var s = this.getScrollState();
57210         this.refresh();
57211         this.restoreScroll(s);
57212     },
57213
57214     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57215         if(!noRefresh){
57216            this.refresh();
57217         }
57218         this.updateHeaderSortState();
57219     },
57220
57221     getScrollState : function(){
57222         
57223         var sb = this.scroller.dom;
57224         return {left: sb.scrollLeft, top: sb.scrollTop};
57225     },
57226
57227     stripeRows : function(startRow){
57228         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57229             return;
57230         }
57231         startRow = startRow || 0;
57232         var rows = this.getBodyTable().rows;
57233         var lrows = this.getLockedTable().rows;
57234         var cls = ' x-grid-row-alt ';
57235         for(var i = startRow, len = rows.length; i < len; i++){
57236             var row = rows[i], lrow = lrows[i];
57237             var isAlt = ((i+1) % 2 == 0);
57238             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57239             if(isAlt == hasAlt){
57240                 continue;
57241             }
57242             if(isAlt){
57243                 row.className += " x-grid-row-alt";
57244             }else{
57245                 row.className = row.className.replace("x-grid-row-alt", "");
57246             }
57247             if(lrow){
57248                 lrow.className = row.className;
57249             }
57250         }
57251     },
57252
57253     restoreScroll : function(state){
57254         //Roo.log('GridView.restoreScroll');
57255         var sb = this.scroller.dom;
57256         sb.scrollLeft = state.left;
57257         sb.scrollTop = state.top;
57258         this.syncScroll();
57259     },
57260
57261     syncScroll : function(){
57262         //Roo.log('GridView.syncScroll');
57263         var sb = this.scroller.dom;
57264         var sh = this.mainHd.dom;
57265         var bs = this.mainBody.dom;
57266         var lv = this.lockedBody.dom;
57267         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57268         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57269     },
57270
57271     handleScroll : function(e){
57272         this.syncScroll();
57273         var sb = this.scroller.dom;
57274         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57275         e.stopEvent();
57276     },
57277
57278     handleWheel : function(e){
57279         var d = e.getWheelDelta();
57280         this.scroller.dom.scrollTop -= d*22;
57281         // set this here to prevent jumpy scrolling on large tables
57282         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57283         e.stopEvent();
57284     },
57285
57286     renderRows : function(startRow, endRow){
57287         // pull in all the crap needed to render rows
57288         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57289         var colCount = cm.getColumnCount();
57290
57291         if(ds.getCount() < 1){
57292             return ["", ""];
57293         }
57294
57295         // build a map for all the columns
57296         var cs = [];
57297         for(var i = 0; i < colCount; i++){
57298             var name = cm.getDataIndex(i);
57299             cs[i] = {
57300                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57301                 renderer : cm.getRenderer(i),
57302                 id : cm.getColumnId(i),
57303                 locked : cm.isLocked(i),
57304                 has_editor : cm.isCellEditable(i)
57305             };
57306         }
57307
57308         startRow = startRow || 0;
57309         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57310
57311         // records to render
57312         var rs = ds.getRange(startRow, endRow);
57313
57314         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57315     },
57316
57317     // As much as I hate to duplicate code, this was branched because FireFox really hates
57318     // [].join("") on strings. The performance difference was substantial enough to
57319     // branch this function
57320     doRender : Roo.isGecko ?
57321             function(cs, rs, ds, startRow, colCount, stripe){
57322                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57323                 // buffers
57324                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57325                 
57326                 var hasListener = this.grid.hasListener('rowclass');
57327                 var rowcfg = {};
57328                 for(var j = 0, len = rs.length; j < len; j++){
57329                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57330                     for(var i = 0; i < colCount; i++){
57331                         c = cs[i];
57332                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57333                         p.id = c.id;
57334                         p.css = p.attr = "";
57335                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57336                         if(p.value == undefined || p.value === "") {
57337                             p.value = "&#160;";
57338                         }
57339                         if(c.has_editor){
57340                             p.css += ' x-grid-editable-cell';
57341                         }
57342                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57343                             p.css +=  ' x-grid-dirty-cell';
57344                         }
57345                         var markup = ct.apply(p);
57346                         if(!c.locked){
57347                             cb+= markup;
57348                         }else{
57349                             lcb+= markup;
57350                         }
57351                     }
57352                     var alt = [];
57353                     if(stripe && ((rowIndex+1) % 2 == 0)){
57354                         alt.push("x-grid-row-alt")
57355                     }
57356                     if(r.dirty){
57357                         alt.push(  " x-grid-dirty-row");
57358                     }
57359                     rp.cells = lcb;
57360                     if(this.getRowClass){
57361                         alt.push(this.getRowClass(r, rowIndex));
57362                     }
57363                     if (hasListener) {
57364                         rowcfg = {
57365                              
57366                             record: r,
57367                             rowIndex : rowIndex,
57368                             rowClass : ''
57369                         };
57370                         this.grid.fireEvent('rowclass', this, rowcfg);
57371                         alt.push(rowcfg.rowClass);
57372                     }
57373                     rp.alt = alt.join(" ");
57374                     lbuf+= rt.apply(rp);
57375                     rp.cells = cb;
57376                     buf+=  rt.apply(rp);
57377                 }
57378                 return [lbuf, buf];
57379             } :
57380             function(cs, rs, ds, startRow, colCount, stripe){
57381                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57382                 // buffers
57383                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57384                 var hasListener = this.grid.hasListener('rowclass');
57385  
57386                 var rowcfg = {};
57387                 for(var j = 0, len = rs.length; j < len; j++){
57388                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57389                     for(var i = 0; i < colCount; i++){
57390                         c = cs[i];
57391                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57392                         p.id = c.id;
57393                         p.css = p.attr = "";
57394                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57395                         if(p.value == undefined || p.value === "") {
57396                             p.value = "&#160;";
57397                         }
57398                         //Roo.log(c);
57399                          if(c.has_editor){
57400                             p.css += ' x-grid-editable-cell';
57401                         }
57402                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57403                             p.css += ' x-grid-dirty-cell' 
57404                         }
57405                         
57406                         var markup = ct.apply(p);
57407                         if(!c.locked){
57408                             cb[cb.length] = markup;
57409                         }else{
57410                             lcb[lcb.length] = markup;
57411                         }
57412                     }
57413                     var alt = [];
57414                     if(stripe && ((rowIndex+1) % 2 == 0)){
57415                         alt.push( "x-grid-row-alt");
57416                     }
57417                     if(r.dirty){
57418                         alt.push(" x-grid-dirty-row");
57419                     }
57420                     rp.cells = lcb;
57421                     if(this.getRowClass){
57422                         alt.push( this.getRowClass(r, rowIndex));
57423                     }
57424                     if (hasListener) {
57425                         rowcfg = {
57426                              
57427                             record: r,
57428                             rowIndex : rowIndex,
57429                             rowClass : ''
57430                         };
57431                         this.grid.fireEvent('rowclass', this, rowcfg);
57432                         alt.push(rowcfg.rowClass);
57433                     }
57434                     
57435                     rp.alt = alt.join(" ");
57436                     rp.cells = lcb.join("");
57437                     lbuf[lbuf.length] = rt.apply(rp);
57438                     rp.cells = cb.join("");
57439                     buf[buf.length] =  rt.apply(rp);
57440                 }
57441                 return [lbuf.join(""), buf.join("")];
57442             },
57443
57444     renderBody : function(){
57445         var markup = this.renderRows();
57446         var bt = this.templates.body;
57447         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57448     },
57449
57450     /**
57451      * Refreshes the grid
57452      * @param {Boolean} headersToo
57453      */
57454     refresh : function(headersToo){
57455         this.fireEvent("beforerefresh", this);
57456         this.grid.stopEditing();
57457         var result = this.renderBody();
57458         this.lockedBody.update(result[0]);
57459         this.mainBody.update(result[1]);
57460         if(headersToo === true){
57461             this.updateHeaders();
57462             this.updateColumns();
57463             this.updateSplitters();
57464             this.updateHeaderSortState();
57465         }
57466         this.syncRowHeights();
57467         this.layout();
57468         this.fireEvent("refresh", this);
57469     },
57470
57471     handleColumnMove : function(cm, oldIndex, newIndex){
57472         this.indexMap = null;
57473         var s = this.getScrollState();
57474         this.refresh(true);
57475         this.restoreScroll(s);
57476         this.afterMove(newIndex);
57477     },
57478
57479     afterMove : function(colIndex){
57480         if(this.enableMoveAnim && Roo.enableFx){
57481             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57482         }
57483         // if multisort - fix sortOrder, and reload..
57484         if (this.grid.dataSource.multiSort) {
57485             // the we can call sort again..
57486             var dm = this.grid.dataSource;
57487             var cm = this.grid.colModel;
57488             var so = [];
57489             for(var i = 0; i < cm.config.length; i++ ) {
57490                 
57491                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57492                     continue; // dont' bother, it's not in sort list or being set.
57493                 }
57494                 
57495                 so.push(cm.config[i].dataIndex);
57496             };
57497             dm.sortOrder = so;
57498             dm.load(dm.lastOptions);
57499             
57500             
57501         }
57502         
57503     },
57504
57505     updateCell : function(dm, rowIndex, dataIndex){
57506         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57507         if(typeof colIndex == "undefined"){ // not present in grid
57508             return;
57509         }
57510         var cm = this.grid.colModel;
57511         var cell = this.getCell(rowIndex, colIndex);
57512         var cellText = this.getCellText(rowIndex, colIndex);
57513
57514         var p = {
57515             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57516             id : cm.getColumnId(colIndex),
57517             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57518         };
57519         var renderer = cm.getRenderer(colIndex);
57520         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57521         if(typeof val == "undefined" || val === "") {
57522             val = "&#160;";
57523         }
57524         cellText.innerHTML = val;
57525         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57526         this.syncRowHeights(rowIndex, rowIndex);
57527     },
57528
57529     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57530         var maxWidth = 0;
57531         if(this.grid.autoSizeHeaders){
57532             var h = this.getHeaderCellMeasure(colIndex);
57533             maxWidth = Math.max(maxWidth, h.scrollWidth);
57534         }
57535         var tb, index;
57536         if(this.cm.isLocked(colIndex)){
57537             tb = this.getLockedTable();
57538             index = colIndex;
57539         }else{
57540             tb = this.getBodyTable();
57541             index = colIndex - this.cm.getLockedCount();
57542         }
57543         if(tb && tb.rows){
57544             var rows = tb.rows;
57545             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57546             for(var i = 0; i < stopIndex; i++){
57547                 var cell = rows[i].childNodes[index].firstChild;
57548                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57549             }
57550         }
57551         return maxWidth + /*margin for error in IE*/ 5;
57552     },
57553     /**
57554      * Autofit a column to its content.
57555      * @param {Number} colIndex
57556      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57557      */
57558      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57559          if(this.cm.isHidden(colIndex)){
57560              return; // can't calc a hidden column
57561          }
57562         if(forceMinSize){
57563             var cid = this.cm.getColumnId(colIndex);
57564             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57565            if(this.grid.autoSizeHeaders){
57566                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57567            }
57568         }
57569         var newWidth = this.calcColumnWidth(colIndex);
57570         this.cm.setColumnWidth(colIndex,
57571             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57572         if(!suppressEvent){
57573             this.grid.fireEvent("columnresize", colIndex, newWidth);
57574         }
57575     },
57576
57577     /**
57578      * Autofits all columns to their content and then expands to fit any extra space in the grid
57579      */
57580      autoSizeColumns : function(){
57581         var cm = this.grid.colModel;
57582         var colCount = cm.getColumnCount();
57583         for(var i = 0; i < colCount; i++){
57584             this.autoSizeColumn(i, true, true);
57585         }
57586         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57587             this.fitColumns();
57588         }else{
57589             this.updateColumns();
57590             this.layout();
57591         }
57592     },
57593
57594     /**
57595      * Autofits all columns to the grid's width proportionate with their current size
57596      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57597      */
57598     fitColumns : function(reserveScrollSpace){
57599         var cm = this.grid.colModel;
57600         var colCount = cm.getColumnCount();
57601         var cols = [];
57602         var width = 0;
57603         var i, w;
57604         for (i = 0; i < colCount; i++){
57605             if(!cm.isHidden(i) && !cm.isFixed(i)){
57606                 w = cm.getColumnWidth(i);
57607                 cols.push(i);
57608                 cols.push(w);
57609                 width += w;
57610             }
57611         }
57612         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57613         if(reserveScrollSpace){
57614             avail -= 17;
57615         }
57616         var frac = (avail - cm.getTotalWidth())/width;
57617         while (cols.length){
57618             w = cols.pop();
57619             i = cols.pop();
57620             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57621         }
57622         this.updateColumns();
57623         this.layout();
57624     },
57625
57626     onRowSelect : function(rowIndex){
57627         var row = this.getRowComposite(rowIndex);
57628         row.addClass("x-grid-row-selected");
57629     },
57630
57631     onRowDeselect : function(rowIndex){
57632         var row = this.getRowComposite(rowIndex);
57633         row.removeClass("x-grid-row-selected");
57634     },
57635
57636     onCellSelect : function(row, col){
57637         var cell = this.getCell(row, col);
57638         if(cell){
57639             Roo.fly(cell).addClass("x-grid-cell-selected");
57640         }
57641     },
57642
57643     onCellDeselect : function(row, col){
57644         var cell = this.getCell(row, col);
57645         if(cell){
57646             Roo.fly(cell).removeClass("x-grid-cell-selected");
57647         }
57648     },
57649
57650     updateHeaderSortState : function(){
57651         
57652         // sort state can be single { field: xxx, direction : yyy}
57653         // or   { xxx=>ASC , yyy : DESC ..... }
57654         
57655         var mstate = {};
57656         if (!this.ds.multiSort) { 
57657             var state = this.ds.getSortState();
57658             if(!state){
57659                 return;
57660             }
57661             mstate[state.field] = state.direction;
57662             // FIXME... - this is not used here.. but might be elsewhere..
57663             this.sortState = state;
57664             
57665         } else {
57666             mstate = this.ds.sortToggle;
57667         }
57668         //remove existing sort classes..
57669         
57670         var sc = this.sortClasses;
57671         var hds = this.el.select(this.headerSelector).removeClass(sc);
57672         
57673         for(var f in mstate) {
57674         
57675             var sortColumn = this.cm.findColumnIndex(f);
57676             
57677             if(sortColumn != -1){
57678                 var sortDir = mstate[f];        
57679                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57680             }
57681         }
57682         
57683          
57684         
57685     },
57686
57687
57688     handleHeaderClick : function(g, index,e){
57689         
57690         Roo.log("header click");
57691         
57692         if (Roo.isTouch) {
57693             // touch events on header are handled by context
57694             this.handleHdCtx(g,index,e);
57695             return;
57696         }
57697         
57698         
57699         if(this.headersDisabled){
57700             return;
57701         }
57702         var dm = g.dataSource, cm = g.colModel;
57703         if(!cm.isSortable(index)){
57704             return;
57705         }
57706         g.stopEditing();
57707         
57708         if (dm.multiSort) {
57709             // update the sortOrder
57710             var so = [];
57711             for(var i = 0; i < cm.config.length; i++ ) {
57712                 
57713                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57714                     continue; // dont' bother, it's not in sort list or being set.
57715                 }
57716                 
57717                 so.push(cm.config[i].dataIndex);
57718             };
57719             dm.sortOrder = so;
57720         }
57721         
57722         
57723         dm.sort(cm.getDataIndex(index));
57724     },
57725
57726
57727     destroy : function(){
57728         if(this.colMenu){
57729             this.colMenu.removeAll();
57730             Roo.menu.MenuMgr.unregister(this.colMenu);
57731             this.colMenu.getEl().remove();
57732             delete this.colMenu;
57733         }
57734         if(this.hmenu){
57735             this.hmenu.removeAll();
57736             Roo.menu.MenuMgr.unregister(this.hmenu);
57737             this.hmenu.getEl().remove();
57738             delete this.hmenu;
57739         }
57740         if(this.grid.enableColumnMove){
57741             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57742             if(dds){
57743                 for(var dd in dds){
57744                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57745                         var elid = dds[dd].dragElId;
57746                         dds[dd].unreg();
57747                         Roo.get(elid).remove();
57748                     } else if(dds[dd].config.isTarget){
57749                         dds[dd].proxyTop.remove();
57750                         dds[dd].proxyBottom.remove();
57751                         dds[dd].unreg();
57752                     }
57753                     if(Roo.dd.DDM.locationCache[dd]){
57754                         delete Roo.dd.DDM.locationCache[dd];
57755                     }
57756                 }
57757                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57758             }
57759         }
57760         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57761         this.bind(null, null);
57762         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57763     },
57764
57765     handleLockChange : function(){
57766         this.refresh(true);
57767     },
57768
57769     onDenyColumnLock : function(){
57770
57771     },
57772
57773     onDenyColumnHide : function(){
57774
57775     },
57776
57777     handleHdMenuClick : function(item){
57778         var index = this.hdCtxIndex;
57779         var cm = this.cm, ds = this.ds;
57780         switch(item.id){
57781             case "asc":
57782                 ds.sort(cm.getDataIndex(index), "ASC");
57783                 break;
57784             case "desc":
57785                 ds.sort(cm.getDataIndex(index), "DESC");
57786                 break;
57787             case "lock":
57788                 var lc = cm.getLockedCount();
57789                 if(cm.getColumnCount(true) <= lc+1){
57790                     this.onDenyColumnLock();
57791                     return;
57792                 }
57793                 if(lc != index){
57794                     cm.setLocked(index, true, true);
57795                     cm.moveColumn(index, lc);
57796                     this.grid.fireEvent("columnmove", index, lc);
57797                 }else{
57798                     cm.setLocked(index, true);
57799                 }
57800             break;
57801             case "unlock":
57802                 var lc = cm.getLockedCount();
57803                 if((lc-1) != index){
57804                     cm.setLocked(index, false, true);
57805                     cm.moveColumn(index, lc-1);
57806                     this.grid.fireEvent("columnmove", index, lc-1);
57807                 }else{
57808                     cm.setLocked(index, false);
57809                 }
57810             break;
57811             case 'wider': // used to expand cols on touch..
57812             case 'narrow':
57813                 var cw = cm.getColumnWidth(index);
57814                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57815                 cw = Math.max(0, cw);
57816                 cw = Math.min(cw,4000);
57817                 cm.setColumnWidth(index, cw);
57818                 break;
57819                 
57820             default:
57821                 index = cm.getIndexById(item.id.substr(4));
57822                 if(index != -1){
57823                     if(item.checked && cm.getColumnCount(true) <= 1){
57824                         this.onDenyColumnHide();
57825                         return false;
57826                     }
57827                     cm.setHidden(index, item.checked);
57828                 }
57829         }
57830         return true;
57831     },
57832
57833     beforeColMenuShow : function(){
57834         var cm = this.cm,  colCount = cm.getColumnCount();
57835         this.colMenu.removeAll();
57836         for(var i = 0; i < colCount; i++){
57837             this.colMenu.add(new Roo.menu.CheckItem({
57838                 id: "col-"+cm.getColumnId(i),
57839                 text: cm.getColumnHeader(i),
57840                 checked: !cm.isHidden(i),
57841                 hideOnClick:false
57842             }));
57843         }
57844     },
57845
57846     handleHdCtx : function(g, index, e){
57847         e.stopEvent();
57848         var hd = this.getHeaderCell(index);
57849         this.hdCtxIndex = index;
57850         var ms = this.hmenu.items, cm = this.cm;
57851         ms.get("asc").setDisabled(!cm.isSortable(index));
57852         ms.get("desc").setDisabled(!cm.isSortable(index));
57853         if(this.grid.enableColLock !== false){
57854             ms.get("lock").setDisabled(cm.isLocked(index));
57855             ms.get("unlock").setDisabled(!cm.isLocked(index));
57856         }
57857         this.hmenu.show(hd, "tl-bl");
57858     },
57859
57860     handleHdOver : function(e){
57861         var hd = this.findHeaderCell(e.getTarget());
57862         if(hd && !this.headersDisabled){
57863             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57864                this.fly(hd).addClass("x-grid-hd-over");
57865             }
57866         }
57867     },
57868
57869     handleHdOut : function(e){
57870         var hd = this.findHeaderCell(e.getTarget());
57871         if(hd){
57872             this.fly(hd).removeClass("x-grid-hd-over");
57873         }
57874     },
57875
57876     handleSplitDblClick : function(e, t){
57877         var i = this.getCellIndex(t);
57878         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57879             this.autoSizeColumn(i, true);
57880             this.layout();
57881         }
57882     },
57883
57884     render : function(){
57885
57886         var cm = this.cm;
57887         var colCount = cm.getColumnCount();
57888
57889         if(this.grid.monitorWindowResize === true){
57890             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57891         }
57892         var header = this.renderHeaders();
57893         var body = this.templates.body.apply({rows:""});
57894         var html = this.templates.master.apply({
57895             lockedBody: body,
57896             body: body,
57897             lockedHeader: header[0],
57898             header: header[1]
57899         });
57900
57901         //this.updateColumns();
57902
57903         this.grid.getGridEl().dom.innerHTML = html;
57904
57905         this.initElements();
57906         
57907         // a kludge to fix the random scolling effect in webkit
57908         this.el.on("scroll", function() {
57909             this.el.dom.scrollTop=0; // hopefully not recursive..
57910         },this);
57911
57912         this.scroller.on("scroll", this.handleScroll, this);
57913         this.lockedBody.on("mousewheel", this.handleWheel, this);
57914         this.mainBody.on("mousewheel", this.handleWheel, this);
57915
57916         this.mainHd.on("mouseover", this.handleHdOver, this);
57917         this.mainHd.on("mouseout", this.handleHdOut, this);
57918         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57919                 {delegate: "."+this.splitClass});
57920
57921         this.lockedHd.on("mouseover", this.handleHdOver, this);
57922         this.lockedHd.on("mouseout", this.handleHdOut, this);
57923         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57924                 {delegate: "."+this.splitClass});
57925
57926         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57927             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57928         }
57929
57930         this.updateSplitters();
57931
57932         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57933             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57934             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57935         }
57936
57937         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57938             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57939             this.hmenu.add(
57940                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57941                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57942             );
57943             if(this.grid.enableColLock !== false){
57944                 this.hmenu.add('-',
57945                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57946                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57947                 );
57948             }
57949             if (Roo.isTouch) {
57950                  this.hmenu.add('-',
57951                     {id:"wider", text: this.columnsWiderText},
57952                     {id:"narrow", text: this.columnsNarrowText }
57953                 );
57954                 
57955                  
57956             }
57957             
57958             if(this.grid.enableColumnHide !== false){
57959
57960                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57961                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57962                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57963
57964                 this.hmenu.add('-',
57965                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57966                 );
57967             }
57968             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57969
57970             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57971         }
57972
57973         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57974             this.dd = new Roo.grid.GridDragZone(this.grid, {
57975                 ddGroup : this.grid.ddGroup || 'GridDD'
57976             });
57977             
57978         }
57979
57980         /*
57981         for(var i = 0; i < colCount; i++){
57982             if(cm.isHidden(i)){
57983                 this.hideColumn(i);
57984             }
57985             if(cm.config[i].align){
57986                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57987                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57988             }
57989         }*/
57990         
57991         this.updateHeaderSortState();
57992
57993         this.beforeInitialResize();
57994         this.layout(true);
57995
57996         // two part rendering gives faster view to the user
57997         this.renderPhase2.defer(1, this);
57998     },
57999
58000     renderPhase2 : function(){
58001         // render the rows now
58002         this.refresh();
58003         if(this.grid.autoSizeColumns){
58004             this.autoSizeColumns();
58005         }
58006     },
58007
58008     beforeInitialResize : function(){
58009
58010     },
58011
58012     onColumnSplitterMoved : function(i, w){
58013         this.userResized = true;
58014         var cm = this.grid.colModel;
58015         cm.setColumnWidth(i, w, true);
58016         var cid = cm.getColumnId(i);
58017         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58018         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58019         this.updateSplitters();
58020         this.layout();
58021         this.grid.fireEvent("columnresize", i, w);
58022     },
58023
58024     syncRowHeights : function(startIndex, endIndex){
58025         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58026             startIndex = startIndex || 0;
58027             var mrows = this.getBodyTable().rows;
58028             var lrows = this.getLockedTable().rows;
58029             var len = mrows.length-1;
58030             endIndex = Math.min(endIndex || len, len);
58031             for(var i = startIndex; i <= endIndex; i++){
58032                 var m = mrows[i], l = lrows[i];
58033                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58034                 m.style.height = l.style.height = h + "px";
58035             }
58036         }
58037     },
58038
58039     layout : function(initialRender, is2ndPass)
58040     {
58041         var g = this.grid;
58042         var auto = g.autoHeight;
58043         var scrollOffset = 16;
58044         var c = g.getGridEl(), cm = this.cm,
58045                 expandCol = g.autoExpandColumn,
58046                 gv = this;
58047         //c.beginMeasure();
58048
58049         if(!c.dom.offsetWidth){ // display:none?
58050             if(initialRender){
58051                 this.lockedWrap.show();
58052                 this.mainWrap.show();
58053             }
58054             return;
58055         }
58056
58057         var hasLock = this.cm.isLocked(0);
58058
58059         var tbh = this.headerPanel.getHeight();
58060         var bbh = this.footerPanel.getHeight();
58061
58062         if(auto){
58063             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58064             var newHeight = ch + c.getBorderWidth("tb");
58065             if(g.maxHeight){
58066                 newHeight = Math.min(g.maxHeight, newHeight);
58067             }
58068             c.setHeight(newHeight);
58069         }
58070
58071         if(g.autoWidth){
58072             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58073         }
58074
58075         var s = this.scroller;
58076
58077         var csize = c.getSize(true);
58078
58079         this.el.setSize(csize.width, csize.height);
58080
58081         this.headerPanel.setWidth(csize.width);
58082         this.footerPanel.setWidth(csize.width);
58083
58084         var hdHeight = this.mainHd.getHeight();
58085         var vw = csize.width;
58086         var vh = csize.height - (tbh + bbh);
58087
58088         s.setSize(vw, vh);
58089
58090         var bt = this.getBodyTable();
58091         
58092         if(cm.getLockedCount() == cm.config.length){
58093             bt = this.getLockedTable();
58094         }
58095         
58096         var ltWidth = hasLock ?
58097                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58098
58099         var scrollHeight = bt.offsetHeight;
58100         var scrollWidth = ltWidth + bt.offsetWidth;
58101         var vscroll = false, hscroll = false;
58102
58103         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58104
58105         var lw = this.lockedWrap, mw = this.mainWrap;
58106         var lb = this.lockedBody, mb = this.mainBody;
58107
58108         setTimeout(function(){
58109             var t = s.dom.offsetTop;
58110             var w = s.dom.clientWidth,
58111                 h = s.dom.clientHeight;
58112
58113             lw.setTop(t);
58114             lw.setSize(ltWidth, h);
58115
58116             mw.setLeftTop(ltWidth, t);
58117             mw.setSize(w-ltWidth, h);
58118
58119             lb.setHeight(h-hdHeight);
58120             mb.setHeight(h-hdHeight);
58121
58122             if(is2ndPass !== true && !gv.userResized && expandCol){
58123                 // high speed resize without full column calculation
58124                 
58125                 var ci = cm.getIndexById(expandCol);
58126                 if (ci < 0) {
58127                     ci = cm.findColumnIndex(expandCol);
58128                 }
58129                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58130                 var expandId = cm.getColumnId(ci);
58131                 var  tw = cm.getTotalWidth(false);
58132                 var currentWidth = cm.getColumnWidth(ci);
58133                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58134                 if(currentWidth != cw){
58135                     cm.setColumnWidth(ci, cw, true);
58136                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58137                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58138                     gv.updateSplitters();
58139                     gv.layout(false, true);
58140                 }
58141             }
58142
58143             if(initialRender){
58144                 lw.show();
58145                 mw.show();
58146             }
58147             //c.endMeasure();
58148         }, 10);
58149     },
58150
58151     onWindowResize : function(){
58152         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58153             return;
58154         }
58155         this.layout();
58156     },
58157
58158     appendFooter : function(parentEl){
58159         return null;
58160     },
58161
58162     sortAscText : "Sort Ascending",
58163     sortDescText : "Sort Descending",
58164     lockText : "Lock Column",
58165     unlockText : "Unlock Column",
58166     columnsText : "Columns",
58167  
58168     columnsWiderText : "Wider",
58169     columnsNarrowText : "Thinner"
58170 });
58171
58172
58173 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58174     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58175     this.proxy.el.addClass('x-grid3-col-dd');
58176 };
58177
58178 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58179     handleMouseDown : function(e){
58180
58181     },
58182
58183     callHandleMouseDown : function(e){
58184         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58185     }
58186 });
58187 /*
58188  * Based on:
58189  * Ext JS Library 1.1.1
58190  * Copyright(c) 2006-2007, Ext JS, LLC.
58191  *
58192  * Originally Released Under LGPL - original licence link has changed is not relivant.
58193  *
58194  * Fork - LGPL
58195  * <script type="text/javascript">
58196  */
58197  /**
58198  * @extends Roo.dd.DDProxy
58199  * @class Roo.grid.SplitDragZone
58200  * Support for Column Header resizing
58201  * @constructor
58202  * @param {Object} config
58203  */
58204 // private
58205 // This is a support class used internally by the Grid components
58206 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58207     this.grid = grid;
58208     this.view = grid.getView();
58209     this.proxy = this.view.resizeProxy;
58210     Roo.grid.SplitDragZone.superclass.constructor.call(
58211         this,
58212         hd, // ID
58213         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58214         {  // CONFIG
58215             dragElId : Roo.id(this.proxy.dom),
58216             resizeFrame:false
58217         }
58218     );
58219     
58220     this.setHandleElId(Roo.id(hd));
58221     if (hd2 !== false) {
58222         this.setOuterHandleElId(Roo.id(hd2));
58223     }
58224     
58225     this.scroll = false;
58226 };
58227 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58228     fly: Roo.Element.fly,
58229
58230     b4StartDrag : function(x, y){
58231         this.view.headersDisabled = true;
58232         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58233                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58234         );
58235         this.proxy.setHeight(h);
58236         
58237         // for old system colWidth really stored the actual width?
58238         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58239         // which in reality did not work.. - it worked only for fixed sizes
58240         // for resizable we need to use actual sizes.
58241         var w = this.cm.getColumnWidth(this.cellIndex);
58242         if (!this.view.mainWrap) {
58243             // bootstrap.
58244             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58245         }
58246         
58247         
58248         
58249         // this was w-this.grid.minColumnWidth;
58250         // doesnt really make sense? - w = thie curren width or the rendered one?
58251         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58252         this.resetConstraints();
58253         this.setXConstraint(minw, 1000);
58254         this.setYConstraint(0, 0);
58255         this.minX = x - minw;
58256         this.maxX = x + 1000;
58257         this.startPos = x;
58258         if (!this.view.mainWrap) { // this is Bootstrap code..
58259             this.getDragEl().style.display='block';
58260         }
58261         
58262         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58263     },
58264
58265
58266     handleMouseDown : function(e){
58267         ev = Roo.EventObject.setEvent(e);
58268         var t = this.fly(ev.getTarget());
58269         if(t.hasClass("x-grid-split")){
58270             this.cellIndex = this.view.getCellIndex(t.dom);
58271             this.split = t.dom;
58272             this.cm = this.grid.colModel;
58273             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58274                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58275             }
58276         }
58277     },
58278
58279     endDrag : function(e){
58280         this.view.headersDisabled = false;
58281         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58282         var diff = endX - this.startPos;
58283         // 
58284         var w = this.cm.getColumnWidth(this.cellIndex);
58285         if (!this.view.mainWrap) {
58286             w = 0;
58287         }
58288         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58289     },
58290
58291     autoOffset : function(){
58292         this.setDelta(0,0);
58293     }
58294 });/*
58295  * Based on:
58296  * Ext JS Library 1.1.1
58297  * Copyright(c) 2006-2007, Ext JS, LLC.
58298  *
58299  * Originally Released Under LGPL - original licence link has changed is not relivant.
58300  *
58301  * Fork - LGPL
58302  * <script type="text/javascript">
58303  */
58304  
58305 // private
58306 // This is a support class used internally by the Grid components
58307 Roo.grid.GridDragZone = function(grid, config){
58308     this.view = grid.getView();
58309     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58310     if(this.view.lockedBody){
58311         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58312         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58313     }
58314     this.scroll = false;
58315     this.grid = grid;
58316     this.ddel = document.createElement('div');
58317     this.ddel.className = 'x-grid-dd-wrap';
58318 };
58319
58320 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58321     ddGroup : "GridDD",
58322
58323     getDragData : function(e){
58324         var t = Roo.lib.Event.getTarget(e);
58325         var rowIndex = this.view.findRowIndex(t);
58326         var sm = this.grid.selModel;
58327             
58328         //Roo.log(rowIndex);
58329         
58330         if (sm.getSelectedCell) {
58331             // cell selection..
58332             if (!sm.getSelectedCell()) {
58333                 return false;
58334             }
58335             if (rowIndex != sm.getSelectedCell()[0]) {
58336                 return false;
58337             }
58338         
58339         }
58340         if (sm.getSelections && sm.getSelections().length < 1) {
58341             return false;
58342         }
58343         
58344         
58345         // before it used to all dragging of unseleted... - now we dont do that.
58346         if(rowIndex !== false){
58347             
58348             // if editorgrid.. 
58349             
58350             
58351             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58352                
58353             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58354               //  
58355             //}
58356             if (e.hasModifier()){
58357                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58358             }
58359             
58360             Roo.log("getDragData");
58361             
58362             return {
58363                 grid: this.grid,
58364                 ddel: this.ddel,
58365                 rowIndex: rowIndex,
58366                 selections: sm.getSelections ? sm.getSelections() : (
58367                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58368             };
58369         }
58370         return false;
58371     },
58372     
58373     
58374     onInitDrag : function(e){
58375         var data = this.dragData;
58376         this.ddel.innerHTML = this.grid.getDragDropText();
58377         this.proxy.update(this.ddel);
58378         // fire start drag?
58379     },
58380
58381     afterRepair : function(){
58382         this.dragging = false;
58383     },
58384
58385     getRepairXY : function(e, data){
58386         return false;
58387     },
58388
58389     onEndDrag : function(data, e){
58390         // fire end drag?
58391     },
58392
58393     onValidDrop : function(dd, e, id){
58394         // fire drag drop?
58395         this.hideProxy();
58396     },
58397
58398     beforeInvalidDrop : function(e, id){
58399
58400     }
58401 });/*
58402  * Based on:
58403  * Ext JS Library 1.1.1
58404  * Copyright(c) 2006-2007, Ext JS, LLC.
58405  *
58406  * Originally Released Under LGPL - original licence link has changed is not relivant.
58407  *
58408  * Fork - LGPL
58409  * <script type="text/javascript">
58410  */
58411  
58412
58413 /**
58414  * @class Roo.grid.ColumnModel
58415  * @extends Roo.util.Observable
58416  * This is the default implementation of a ColumnModel used by the Grid. It defines
58417  * the columns in the grid.
58418  * <br>Usage:<br>
58419  <pre><code>
58420  var colModel = new Roo.grid.ColumnModel([
58421         {header: "Ticker", width: 60, sortable: true, locked: true},
58422         {header: "Company Name", width: 150, sortable: true},
58423         {header: "Market Cap.", width: 100, sortable: true},
58424         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58425         {header: "Employees", width: 100, sortable: true, resizable: false}
58426  ]);
58427  </code></pre>
58428  * <p>
58429  
58430  * The config options listed for this class are options which may appear in each
58431  * individual column definition.
58432  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58433  * @constructor
58434  * @param {Object} config An Array of column config objects. See this class's
58435  * config objects for details.
58436 */
58437 Roo.grid.ColumnModel = function(config){
58438         /**
58439      * The config passed into the constructor
58440      */
58441     this.config = []; //config;
58442     this.lookup = {};
58443
58444     // if no id, create one
58445     // if the column does not have a dataIndex mapping,
58446     // map it to the order it is in the config
58447     for(var i = 0, len = config.length; i < len; i++){
58448         this.addColumn(config[i]);
58449         
58450     }
58451
58452     /**
58453      * The width of columns which have no width specified (defaults to 100)
58454      * @type Number
58455      */
58456     this.defaultWidth = 100;
58457
58458     /**
58459      * Default sortable of columns which have no sortable specified (defaults to false)
58460      * @type Boolean
58461      */
58462     this.defaultSortable = false;
58463
58464     this.addEvents({
58465         /**
58466              * @event widthchange
58467              * Fires when the width of a column changes.
58468              * @param {ColumnModel} this
58469              * @param {Number} columnIndex The column index
58470              * @param {Number} newWidth The new width
58471              */
58472             "widthchange": true,
58473         /**
58474              * @event headerchange
58475              * Fires when the text of a header changes.
58476              * @param {ColumnModel} this
58477              * @param {Number} columnIndex The column index
58478              * @param {Number} newText The new header text
58479              */
58480             "headerchange": true,
58481         /**
58482              * @event hiddenchange
58483              * Fires when a column is hidden or "unhidden".
58484              * @param {ColumnModel} this
58485              * @param {Number} columnIndex The column index
58486              * @param {Boolean} hidden true if hidden, false otherwise
58487              */
58488             "hiddenchange": true,
58489             /**
58490          * @event columnmoved
58491          * Fires when a column is moved.
58492          * @param {ColumnModel} this
58493          * @param {Number} oldIndex
58494          * @param {Number} newIndex
58495          */
58496         "columnmoved" : true,
58497         /**
58498          * @event columlockchange
58499          * Fires when a column's locked state is changed
58500          * @param {ColumnModel} this
58501          * @param {Number} colIndex
58502          * @param {Boolean} locked true if locked
58503          */
58504         "columnlockchange" : true
58505     });
58506     Roo.grid.ColumnModel.superclass.constructor.call(this);
58507 };
58508 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58509     /**
58510      * @cfg {String} header The header text to display in the Grid view.
58511      */
58512         /**
58513      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58514      */
58515         /**
58516      * @cfg {String} smHeader Header at Bootsrap Small width
58517      */
58518         /**
58519      * @cfg {String} mdHeader Header at Bootsrap Medium width
58520      */
58521         /**
58522      * @cfg {String} lgHeader Header at Bootsrap Large width
58523      */
58524         /**
58525      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58526      */
58527     /**
58528      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58529      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58530      * specified, the column's index is used as an index into the Record's data Array.
58531      */
58532     /**
58533      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58534      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58535      */
58536     /**
58537      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58538      * Defaults to the value of the {@link #defaultSortable} property.
58539      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58540      */
58541     /**
58542      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58543      */
58544     /**
58545      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58546      */
58547     /**
58548      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58549      */
58550     /**
58551      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58552      */
58553     /**
58554      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58555      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58556      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58557      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58558      */
58559        /**
58560      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58561      */
58562     /**
58563      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58564      */
58565     /**
58566      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58567      */
58568     /**
58569      * @cfg {String} cursor (Optional)
58570      */
58571     /**
58572      * @cfg {String} tooltip (Optional)
58573      */
58574     /**
58575      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58576      */
58577     /**
58578      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58579      */
58580     /**
58581      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58582      */
58583     /**
58584      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58585      */
58586         /**
58587      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58588      */
58589     /**
58590      * Returns the id of the column at the specified index.
58591      * @param {Number} index The column index
58592      * @return {String} the id
58593      */
58594     getColumnId : function(index){
58595         return this.config[index].id;
58596     },
58597
58598     /**
58599      * Returns the column for a specified id.
58600      * @param {String} id The column id
58601      * @return {Object} the column
58602      */
58603     getColumnById : function(id){
58604         return this.lookup[id];
58605     },
58606
58607     
58608     /**
58609      * Returns the column Object for a specified dataIndex.
58610      * @param {String} dataIndex The column dataIndex
58611      * @return {Object|Boolean} the column or false if not found
58612      */
58613     getColumnByDataIndex: function(dataIndex){
58614         var index = this.findColumnIndex(dataIndex);
58615         return index > -1 ? this.config[index] : false;
58616     },
58617     
58618     /**
58619      * Returns the index for a specified column id.
58620      * @param {String} id The column id
58621      * @return {Number} the index, or -1 if not found
58622      */
58623     getIndexById : function(id){
58624         for(var i = 0, len = this.config.length; i < len; i++){
58625             if(this.config[i].id == id){
58626                 return i;
58627             }
58628         }
58629         return -1;
58630     },
58631     
58632     /**
58633      * Returns the index for a specified column dataIndex.
58634      * @param {String} dataIndex The column dataIndex
58635      * @return {Number} the index, or -1 if not found
58636      */
58637     
58638     findColumnIndex : function(dataIndex){
58639         for(var i = 0, len = this.config.length; i < len; i++){
58640             if(this.config[i].dataIndex == dataIndex){
58641                 return i;
58642             }
58643         }
58644         return -1;
58645     },
58646     
58647     
58648     moveColumn : function(oldIndex, newIndex){
58649         var c = this.config[oldIndex];
58650         this.config.splice(oldIndex, 1);
58651         this.config.splice(newIndex, 0, c);
58652         this.dataMap = null;
58653         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58654     },
58655
58656     isLocked : function(colIndex){
58657         return this.config[colIndex].locked === true;
58658     },
58659
58660     setLocked : function(colIndex, value, suppressEvent){
58661         if(this.isLocked(colIndex) == value){
58662             return;
58663         }
58664         this.config[colIndex].locked = value;
58665         if(!suppressEvent){
58666             this.fireEvent("columnlockchange", this, colIndex, value);
58667         }
58668     },
58669
58670     getTotalLockedWidth : function(){
58671         var totalWidth = 0;
58672         for(var i = 0; i < this.config.length; i++){
58673             if(this.isLocked(i) && !this.isHidden(i)){
58674                 this.totalWidth += this.getColumnWidth(i);
58675             }
58676         }
58677         return totalWidth;
58678     },
58679
58680     getLockedCount : function(){
58681         for(var i = 0, len = this.config.length; i < len; i++){
58682             if(!this.isLocked(i)){
58683                 return i;
58684             }
58685         }
58686         
58687         return this.config.length;
58688     },
58689
58690     /**
58691      * Returns the number of columns.
58692      * @return {Number}
58693      */
58694     getColumnCount : function(visibleOnly){
58695         if(visibleOnly === true){
58696             var c = 0;
58697             for(var i = 0, len = this.config.length; i < len; i++){
58698                 if(!this.isHidden(i)){
58699                     c++;
58700                 }
58701             }
58702             return c;
58703         }
58704         return this.config.length;
58705     },
58706
58707     /**
58708      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58709      * @param {Function} fn
58710      * @param {Object} scope (optional)
58711      * @return {Array} result
58712      */
58713     getColumnsBy : function(fn, scope){
58714         var r = [];
58715         for(var i = 0, len = this.config.length; i < len; i++){
58716             var c = this.config[i];
58717             if(fn.call(scope||this, c, i) === true){
58718                 r[r.length] = c;
58719             }
58720         }
58721         return r;
58722     },
58723
58724     /**
58725      * Returns true if the specified column is sortable.
58726      * @param {Number} col The column index
58727      * @return {Boolean}
58728      */
58729     isSortable : function(col){
58730         if(typeof this.config[col].sortable == "undefined"){
58731             return this.defaultSortable;
58732         }
58733         return this.config[col].sortable;
58734     },
58735
58736     /**
58737      * Returns the rendering (formatting) function defined for the column.
58738      * @param {Number} col The column index.
58739      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58740      */
58741     getRenderer : function(col){
58742         if(!this.config[col].renderer){
58743             return Roo.grid.ColumnModel.defaultRenderer;
58744         }
58745         return this.config[col].renderer;
58746     },
58747
58748     /**
58749      * Sets the rendering (formatting) function for a column.
58750      * @param {Number} col The column index
58751      * @param {Function} fn The function to use to process the cell's raw data
58752      * to return HTML markup for the grid view. The render function is called with
58753      * the following parameters:<ul>
58754      * <li>Data value.</li>
58755      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58756      * <li>css A CSS style string to apply to the table cell.</li>
58757      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58758      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58759      * <li>Row index</li>
58760      * <li>Column index</li>
58761      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58762      */
58763     setRenderer : function(col, fn){
58764         this.config[col].renderer = fn;
58765     },
58766
58767     /**
58768      * Returns the width for the specified column.
58769      * @param {Number} col The column index
58770      * @param (optional) {String} gridSize bootstrap width size.
58771      * @return {Number}
58772      */
58773     getColumnWidth : function(col, gridSize)
58774         {
58775                 var cfg = this.config[col];
58776                 
58777                 if (typeof(gridSize) == 'undefined') {
58778                         return cfg.width * 1 || this.defaultWidth;
58779                 }
58780                 if (gridSize === false) { // if we set it..
58781                         return cfg.width || false;
58782                 }
58783                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58784                 
58785                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58786                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58787                                 continue;
58788                         }
58789                         return cfg[ sizes[i] ];
58790                 }
58791                 return 1;
58792                 
58793     },
58794
58795     /**
58796      * Sets the width for a column.
58797      * @param {Number} col The column index
58798      * @param {Number} width The new width
58799      */
58800     setColumnWidth : function(col, width, suppressEvent){
58801         this.config[col].width = width;
58802         this.totalWidth = null;
58803         if(!suppressEvent){
58804              this.fireEvent("widthchange", this, col, width);
58805         }
58806     },
58807
58808     /**
58809      * Returns the total width of all columns.
58810      * @param {Boolean} includeHidden True to include hidden column widths
58811      * @return {Number}
58812      */
58813     getTotalWidth : function(includeHidden){
58814         if(!this.totalWidth){
58815             this.totalWidth = 0;
58816             for(var i = 0, len = this.config.length; i < len; i++){
58817                 if(includeHidden || !this.isHidden(i)){
58818                     this.totalWidth += this.getColumnWidth(i);
58819                 }
58820             }
58821         }
58822         return this.totalWidth;
58823     },
58824
58825     /**
58826      * Returns the header for the specified column.
58827      * @param {Number} col The column index
58828      * @return {String}
58829      */
58830     getColumnHeader : function(col){
58831         return this.config[col].header;
58832     },
58833
58834     /**
58835      * Sets the header for a column.
58836      * @param {Number} col The column index
58837      * @param {String} header The new header
58838      */
58839     setColumnHeader : function(col, header){
58840         this.config[col].header = header;
58841         this.fireEvent("headerchange", this, col, header);
58842     },
58843
58844     /**
58845      * Returns the tooltip for the specified column.
58846      * @param {Number} col The column index
58847      * @return {String}
58848      */
58849     getColumnTooltip : function(col){
58850             return this.config[col].tooltip;
58851     },
58852     /**
58853      * Sets the tooltip for a column.
58854      * @param {Number} col The column index
58855      * @param {String} tooltip The new tooltip
58856      */
58857     setColumnTooltip : function(col, tooltip){
58858             this.config[col].tooltip = tooltip;
58859     },
58860
58861     /**
58862      * Returns the dataIndex for the specified column.
58863      * @param {Number} col The column index
58864      * @return {Number}
58865      */
58866     getDataIndex : function(col){
58867         return this.config[col].dataIndex;
58868     },
58869
58870     /**
58871      * Sets the dataIndex for a column.
58872      * @param {Number} col The column index
58873      * @param {Number} dataIndex The new dataIndex
58874      */
58875     setDataIndex : function(col, dataIndex){
58876         this.config[col].dataIndex = dataIndex;
58877     },
58878
58879     
58880     
58881     /**
58882      * Returns true if the cell is editable.
58883      * @param {Number} colIndex The column index
58884      * @param {Number} rowIndex The row index - this is nto actually used..?
58885      * @return {Boolean}
58886      */
58887     isCellEditable : function(colIndex, rowIndex){
58888         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58889     },
58890
58891     /**
58892      * Returns the editor defined for the cell/column.
58893      * return false or null to disable editing.
58894      * @param {Number} colIndex The column index
58895      * @param {Number} rowIndex The row index
58896      * @return {Object}
58897      */
58898     getCellEditor : function(colIndex, rowIndex){
58899         return this.config[colIndex].editor;
58900     },
58901
58902     /**
58903      * Sets if a column is editable.
58904      * @param {Number} col The column index
58905      * @param {Boolean} editable True if the column is editable
58906      */
58907     setEditable : function(col, editable){
58908         this.config[col].editable = editable;
58909     },
58910
58911
58912     /**
58913      * Returns true if the column is hidden.
58914      * @param {Number} colIndex The column index
58915      * @return {Boolean}
58916      */
58917     isHidden : function(colIndex){
58918         return this.config[colIndex].hidden;
58919     },
58920
58921
58922     /**
58923      * Returns true if the column width cannot be changed
58924      */
58925     isFixed : function(colIndex){
58926         return this.config[colIndex].fixed;
58927     },
58928
58929     /**
58930      * Returns true if the column can be resized
58931      * @return {Boolean}
58932      */
58933     isResizable : function(colIndex){
58934         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58935     },
58936     /**
58937      * Sets if a column is hidden.
58938      * @param {Number} colIndex The column index
58939      * @param {Boolean} hidden True if the column is hidden
58940      */
58941     setHidden : function(colIndex, hidden){
58942         this.config[colIndex].hidden = hidden;
58943         this.totalWidth = null;
58944         this.fireEvent("hiddenchange", this, colIndex, hidden);
58945     },
58946
58947     /**
58948      * Sets the editor for a column.
58949      * @param {Number} col The column index
58950      * @param {Object} editor The editor object
58951      */
58952     setEditor : function(col, editor){
58953         this.config[col].editor = editor;
58954     },
58955     /**
58956      * Add a column (experimental...) - defaults to adding to the end..
58957      * @param {Object} config 
58958     */
58959     addColumn : function(c)
58960     {
58961     
58962         var i = this.config.length;
58963         this.config[i] = c;
58964         
58965         if(typeof c.dataIndex == "undefined"){
58966             c.dataIndex = i;
58967         }
58968         if(typeof c.renderer == "string"){
58969             c.renderer = Roo.util.Format[c.renderer];
58970         }
58971         if(typeof c.id == "undefined"){
58972             c.id = Roo.id();
58973         }
58974         if(c.editor && c.editor.xtype){
58975             c.editor  = Roo.factory(c.editor, Roo.grid);
58976         }
58977         if(c.editor && c.editor.isFormField){
58978             c.editor = new Roo.grid.GridEditor(c.editor);
58979         }
58980         this.lookup[c.id] = c;
58981     }
58982     
58983 });
58984
58985 Roo.grid.ColumnModel.defaultRenderer = function(value)
58986 {
58987     if(typeof value == "object") {
58988         return value;
58989     }
58990         if(typeof value == "string" && value.length < 1){
58991             return "&#160;";
58992         }
58993     
58994         return String.format("{0}", value);
58995 };
58996
58997 // Alias for backwards compatibility
58998 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58999 /*
59000  * Based on:
59001  * Ext JS Library 1.1.1
59002  * Copyright(c) 2006-2007, Ext JS, LLC.
59003  *
59004  * Originally Released Under LGPL - original licence link has changed is not relivant.
59005  *
59006  * Fork - LGPL
59007  * <script type="text/javascript">
59008  */
59009
59010 /**
59011  * @class Roo.grid.AbstractSelectionModel
59012  * @extends Roo.util.Observable
59013  * @abstract
59014  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59015  * implemented by descendant classes.  This class should not be directly instantiated.
59016  * @constructor
59017  */
59018 Roo.grid.AbstractSelectionModel = function(){
59019     this.locked = false;
59020     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59021 };
59022
59023 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59024     /** @ignore Called by the grid automatically. Do not call directly. */
59025     init : function(grid){
59026         this.grid = grid;
59027         this.initEvents();
59028     },
59029
59030     /**
59031      * Locks the selections.
59032      */
59033     lock : function(){
59034         this.locked = true;
59035     },
59036
59037     /**
59038      * Unlocks the selections.
59039      */
59040     unlock : function(){
59041         this.locked = false;
59042     },
59043
59044     /**
59045      * Returns true if the selections are locked.
59046      * @return {Boolean}
59047      */
59048     isLocked : function(){
59049         return this.locked;
59050     }
59051 });/*
59052  * Based on:
59053  * Ext JS Library 1.1.1
59054  * Copyright(c) 2006-2007, Ext JS, LLC.
59055  *
59056  * Originally Released Under LGPL - original licence link has changed is not relivant.
59057  *
59058  * Fork - LGPL
59059  * <script type="text/javascript">
59060  */
59061 /**
59062  * @extends Roo.grid.AbstractSelectionModel
59063  * @class Roo.grid.RowSelectionModel
59064  * The default SelectionModel used by {@link Roo.grid.Grid}.
59065  * It supports multiple selections and keyboard selection/navigation. 
59066  * @constructor
59067  * @param {Object} config
59068  */
59069 Roo.grid.RowSelectionModel = function(config){
59070     Roo.apply(this, config);
59071     this.selections = new Roo.util.MixedCollection(false, function(o){
59072         return o.id;
59073     });
59074
59075     this.last = false;
59076     this.lastActive = false;
59077
59078     this.addEvents({
59079         /**
59080         * @event selectionchange
59081         * Fires when the selection changes
59082         * @param {SelectionModel} this
59083         */
59084        "selectionchange" : true,
59085        /**
59086         * @event afterselectionchange
59087         * Fires after the selection changes (eg. by key press or clicking)
59088         * @param {SelectionModel} this
59089         */
59090        "afterselectionchange" : true,
59091        /**
59092         * @event beforerowselect
59093         * Fires when a row is selected being selected, return false to cancel.
59094         * @param {SelectionModel} this
59095         * @param {Number} rowIndex The selected index
59096         * @param {Boolean} keepExisting False if other selections will be cleared
59097         */
59098        "beforerowselect" : true,
59099        /**
59100         * @event rowselect
59101         * Fires when a row is selected.
59102         * @param {SelectionModel} this
59103         * @param {Number} rowIndex The selected index
59104         * @param {Roo.data.Record} r The record
59105         */
59106        "rowselect" : true,
59107        /**
59108         * @event rowdeselect
59109         * Fires when a row is deselected.
59110         * @param {SelectionModel} this
59111         * @param {Number} rowIndex The selected index
59112         */
59113         "rowdeselect" : true
59114     });
59115     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59116     this.locked = false;
59117 };
59118
59119 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59120     /**
59121      * @cfg {Boolean} singleSelect
59122      * True to allow selection of only one row at a time (defaults to false)
59123      */
59124     singleSelect : false,
59125
59126     // private
59127     initEvents : function(){
59128
59129         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59130             this.grid.on("mousedown", this.handleMouseDown, this);
59131         }else{ // allow click to work like normal
59132             this.grid.on("rowclick", this.handleDragableRowClick, this);
59133         }
59134         // bootstrap does not have a view..
59135         var view = this.grid.view ? this.grid.view : this.grid;
59136         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59137             "up" : function(e){
59138                 if(!e.shiftKey){
59139                     this.selectPrevious(e.shiftKey);
59140                 }else if(this.last !== false && this.lastActive !== false){
59141                     var last = this.last;
59142                     this.selectRange(this.last,  this.lastActive-1);
59143                     view.focusRow(this.lastActive);
59144                     if(last !== false){
59145                         this.last = last;
59146                     }
59147                 }else{
59148                     this.selectFirstRow();
59149                 }
59150                 this.fireEvent("afterselectionchange", this);
59151             },
59152             "down" : function(e){
59153                 if(!e.shiftKey){
59154                     this.selectNext(e.shiftKey);
59155                 }else if(this.last !== false && this.lastActive !== false){
59156                     var last = this.last;
59157                     this.selectRange(this.last,  this.lastActive+1);
59158                     view.focusRow(this.lastActive);
59159                     if(last !== false){
59160                         this.last = last;
59161                     }
59162                 }else{
59163                     this.selectFirstRow();
59164                 }
59165                 this.fireEvent("afterselectionchange", this);
59166             },
59167             scope: this
59168         });
59169
59170          
59171         view.on("refresh", this.onRefresh, this);
59172         view.on("rowupdated", this.onRowUpdated, this);
59173         view.on("rowremoved", this.onRemove, this);
59174     },
59175
59176     // private
59177     onRefresh : function(){
59178         var ds = this.grid.ds, i, v = this.grid.view;
59179         var s = this.selections;
59180         s.each(function(r){
59181             if((i = ds.indexOfId(r.id)) != -1){
59182                 v.onRowSelect(i);
59183                 s.add(ds.getAt(i)); // updating the selection relate data
59184             }else{
59185                 s.remove(r);
59186             }
59187         });
59188     },
59189
59190     // private
59191     onRemove : function(v, index, r){
59192         this.selections.remove(r);
59193     },
59194
59195     // private
59196     onRowUpdated : function(v, index, r){
59197         if(this.isSelected(r)){
59198             v.onRowSelect(index);
59199         }
59200     },
59201
59202     /**
59203      * Select records.
59204      * @param {Array} records The records to select
59205      * @param {Boolean} keepExisting (optional) True to keep existing selections
59206      */
59207     selectRecords : function(records, keepExisting){
59208         if(!keepExisting){
59209             this.clearSelections();
59210         }
59211         var ds = this.grid.ds;
59212         for(var i = 0, len = records.length; i < len; i++){
59213             this.selectRow(ds.indexOf(records[i]), true);
59214         }
59215     },
59216
59217     /**
59218      * Gets the number of selected rows.
59219      * @return {Number}
59220      */
59221     getCount : function(){
59222         return this.selections.length;
59223     },
59224
59225     /**
59226      * Selects the first row in the grid.
59227      */
59228     selectFirstRow : function(){
59229         this.selectRow(0);
59230     },
59231
59232     /**
59233      * Select the last row.
59234      * @param {Boolean} keepExisting (optional) True to keep existing selections
59235      */
59236     selectLastRow : function(keepExisting){
59237         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59238     },
59239
59240     /**
59241      * Selects the row immediately following the last selected row.
59242      * @param {Boolean} keepExisting (optional) True to keep existing selections
59243      */
59244     selectNext : function(keepExisting){
59245         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59246             this.selectRow(this.last+1, keepExisting);
59247             var view = this.grid.view ? this.grid.view : this.grid;
59248             view.focusRow(this.last);
59249         }
59250     },
59251
59252     /**
59253      * Selects the row that precedes the last selected row.
59254      * @param {Boolean} keepExisting (optional) True to keep existing selections
59255      */
59256     selectPrevious : function(keepExisting){
59257         if(this.last){
59258             this.selectRow(this.last-1, keepExisting);
59259             var view = this.grid.view ? this.grid.view : this.grid;
59260             view.focusRow(this.last);
59261         }
59262     },
59263
59264     /**
59265      * Returns the selected records
59266      * @return {Array} Array of selected records
59267      */
59268     getSelections : function(){
59269         return [].concat(this.selections.items);
59270     },
59271
59272     /**
59273      * Returns the first selected record.
59274      * @return {Record}
59275      */
59276     getSelected : function(){
59277         return this.selections.itemAt(0);
59278     },
59279
59280
59281     /**
59282      * Clears all selections.
59283      */
59284     clearSelections : function(fast){
59285         if(this.locked) {
59286             return;
59287         }
59288         if(fast !== true){
59289             var ds = this.grid.ds;
59290             var s = this.selections;
59291             s.each(function(r){
59292                 this.deselectRow(ds.indexOfId(r.id));
59293             }, this);
59294             s.clear();
59295         }else{
59296             this.selections.clear();
59297         }
59298         this.last = false;
59299     },
59300
59301
59302     /**
59303      * Selects all rows.
59304      */
59305     selectAll : function(){
59306         if(this.locked) {
59307             return;
59308         }
59309         this.selections.clear();
59310         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59311             this.selectRow(i, true);
59312         }
59313     },
59314
59315     /**
59316      * Returns True if there is a selection.
59317      * @return {Boolean}
59318      */
59319     hasSelection : function(){
59320         return this.selections.length > 0;
59321     },
59322
59323     /**
59324      * Returns True if the specified row is selected.
59325      * @param {Number/Record} record The record or index of the record to check
59326      * @return {Boolean}
59327      */
59328     isSelected : function(index){
59329         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59330         return (r && this.selections.key(r.id) ? true : false);
59331     },
59332
59333     /**
59334      * Returns True if the specified record id is selected.
59335      * @param {String} id The id of record to check
59336      * @return {Boolean}
59337      */
59338     isIdSelected : function(id){
59339         return (this.selections.key(id) ? true : false);
59340     },
59341
59342     // private
59343     handleMouseDown : function(e, t)
59344     {
59345         var view = this.grid.view ? this.grid.view : this.grid;
59346         var rowIndex;
59347         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59348             return;
59349         };
59350         if(e.shiftKey && this.last !== false){
59351             var last = this.last;
59352             this.selectRange(last, rowIndex, e.ctrlKey);
59353             this.last = last; // reset the last
59354             view.focusRow(rowIndex);
59355         }else{
59356             var isSelected = this.isSelected(rowIndex);
59357             if(e.button !== 0 && isSelected){
59358                 view.focusRow(rowIndex);
59359             }else if(e.ctrlKey && isSelected){
59360                 this.deselectRow(rowIndex);
59361             }else if(!isSelected){
59362                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59363                 view.focusRow(rowIndex);
59364             }
59365         }
59366         this.fireEvent("afterselectionchange", this);
59367     },
59368     // private
59369     handleDragableRowClick :  function(grid, rowIndex, e) 
59370     {
59371         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59372             this.selectRow(rowIndex, false);
59373             var view = this.grid.view ? this.grid.view : this.grid;
59374             view.focusRow(rowIndex);
59375              this.fireEvent("afterselectionchange", this);
59376         }
59377     },
59378     
59379     /**
59380      * Selects multiple rows.
59381      * @param {Array} rows Array of the indexes of the row to select
59382      * @param {Boolean} keepExisting (optional) True to keep existing selections
59383      */
59384     selectRows : function(rows, keepExisting){
59385         if(!keepExisting){
59386             this.clearSelections();
59387         }
59388         for(var i = 0, len = rows.length; i < len; i++){
59389             this.selectRow(rows[i], true);
59390         }
59391     },
59392
59393     /**
59394      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59395      * @param {Number} startRow The index of the first row in the range
59396      * @param {Number} endRow The index of the last row in the range
59397      * @param {Boolean} keepExisting (optional) True to retain existing selections
59398      */
59399     selectRange : function(startRow, endRow, keepExisting){
59400         if(this.locked) {
59401             return;
59402         }
59403         if(!keepExisting){
59404             this.clearSelections();
59405         }
59406         if(startRow <= endRow){
59407             for(var i = startRow; i <= endRow; i++){
59408                 this.selectRow(i, true);
59409             }
59410         }else{
59411             for(var i = startRow; i >= endRow; i--){
59412                 this.selectRow(i, true);
59413             }
59414         }
59415     },
59416
59417     /**
59418      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59419      * @param {Number} startRow The index of the first row in the range
59420      * @param {Number} endRow The index of the last row in the range
59421      */
59422     deselectRange : function(startRow, endRow, preventViewNotify){
59423         if(this.locked) {
59424             return;
59425         }
59426         for(var i = startRow; i <= endRow; i++){
59427             this.deselectRow(i, preventViewNotify);
59428         }
59429     },
59430
59431     /**
59432      * Selects a row.
59433      * @param {Number} row The index of the row to select
59434      * @param {Boolean} keepExisting (optional) True to keep existing selections
59435      */
59436     selectRow : function(index, keepExisting, preventViewNotify){
59437         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59438             return;
59439         }
59440         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59441             if(!keepExisting || this.singleSelect){
59442                 this.clearSelections();
59443             }
59444             var r = this.grid.ds.getAt(index);
59445             this.selections.add(r);
59446             this.last = this.lastActive = index;
59447             if(!preventViewNotify){
59448                 var view = this.grid.view ? this.grid.view : this.grid;
59449                 view.onRowSelect(index);
59450             }
59451             this.fireEvent("rowselect", this, index, r);
59452             this.fireEvent("selectionchange", this);
59453         }
59454     },
59455
59456     /**
59457      * Deselects a row.
59458      * @param {Number} row The index of the row to deselect
59459      */
59460     deselectRow : function(index, preventViewNotify){
59461         if(this.locked) {
59462             return;
59463         }
59464         if(this.last == index){
59465             this.last = false;
59466         }
59467         if(this.lastActive == index){
59468             this.lastActive = false;
59469         }
59470         var r = this.grid.ds.getAt(index);
59471         this.selections.remove(r);
59472         if(!preventViewNotify){
59473             var view = this.grid.view ? this.grid.view : this.grid;
59474             view.onRowDeselect(index);
59475         }
59476         this.fireEvent("rowdeselect", this, index);
59477         this.fireEvent("selectionchange", this);
59478     },
59479
59480     // private
59481     restoreLast : function(){
59482         if(this._last){
59483             this.last = this._last;
59484         }
59485     },
59486
59487     // private
59488     acceptsNav : function(row, col, cm){
59489         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59490     },
59491
59492     // private
59493     onEditorKey : function(field, e){
59494         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59495         if(k == e.TAB){
59496             e.stopEvent();
59497             ed.completeEdit();
59498             if(e.shiftKey){
59499                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59500             }else{
59501                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59502             }
59503         }else if(k == e.ENTER && !e.ctrlKey){
59504             e.stopEvent();
59505             ed.completeEdit();
59506             if(e.shiftKey){
59507                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59508             }else{
59509                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59510             }
59511         }else if(k == e.ESC){
59512             ed.cancelEdit();
59513         }
59514         if(newCell){
59515             g.startEditing(newCell[0], newCell[1]);
59516         }
59517     }
59518 });/*
59519  * Based on:
59520  * Ext JS Library 1.1.1
59521  * Copyright(c) 2006-2007, Ext JS, LLC.
59522  *
59523  * Originally Released Under LGPL - original licence link has changed is not relivant.
59524  *
59525  * Fork - LGPL
59526  * <script type="text/javascript">
59527  */
59528 /**
59529  * @class Roo.grid.CellSelectionModel
59530  * @extends Roo.grid.AbstractSelectionModel
59531  * This class provides the basic implementation for cell selection in a grid.
59532  * @constructor
59533  * @param {Object} config The object containing the configuration of this model.
59534  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59535  */
59536 Roo.grid.CellSelectionModel = function(config){
59537     Roo.apply(this, config);
59538
59539     this.selection = null;
59540
59541     this.addEvents({
59542         /**
59543              * @event beforerowselect
59544              * Fires before a cell is selected.
59545              * @param {SelectionModel} this
59546              * @param {Number} rowIndex The selected row index
59547              * @param {Number} colIndex The selected cell index
59548              */
59549             "beforecellselect" : true,
59550         /**
59551              * @event cellselect
59552              * Fires when a cell is selected.
59553              * @param {SelectionModel} this
59554              * @param {Number} rowIndex The selected row index
59555              * @param {Number} colIndex The selected cell index
59556              */
59557             "cellselect" : true,
59558         /**
59559              * @event selectionchange
59560              * Fires when the active selection changes.
59561              * @param {SelectionModel} this
59562              * @param {Object} selection null for no selection or an object (o) with two properties
59563                 <ul>
59564                 <li>o.record: the record object for the row the selection is in</li>
59565                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59566                 </ul>
59567              */
59568             "selectionchange" : true,
59569         /**
59570              * @event tabend
59571              * Fires when the tab (or enter) was pressed on the last editable cell
59572              * You can use this to trigger add new row.
59573              * @param {SelectionModel} this
59574              */
59575             "tabend" : true,
59576          /**
59577              * @event beforeeditnext
59578              * Fires before the next editable sell is made active
59579              * You can use this to skip to another cell or fire the tabend
59580              *    if you set cell to false
59581              * @param {Object} eventdata object : { cell : [ row, col ] } 
59582              */
59583             "beforeeditnext" : true
59584     });
59585     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59586 };
59587
59588 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59589     
59590     enter_is_tab: false,
59591
59592     /** @ignore */
59593     initEvents : function(){
59594         this.grid.on("mousedown", this.handleMouseDown, this);
59595         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59596         var view = this.grid.view;
59597         view.on("refresh", this.onViewChange, this);
59598         view.on("rowupdated", this.onRowUpdated, this);
59599         view.on("beforerowremoved", this.clearSelections, this);
59600         view.on("beforerowsinserted", this.clearSelections, this);
59601         if(this.grid.isEditor){
59602             this.grid.on("beforeedit", this.beforeEdit,  this);
59603         }
59604     },
59605
59606         //private
59607     beforeEdit : function(e){
59608         this.select(e.row, e.column, false, true, e.record);
59609     },
59610
59611         //private
59612     onRowUpdated : function(v, index, r){
59613         if(this.selection && this.selection.record == r){
59614             v.onCellSelect(index, this.selection.cell[1]);
59615         }
59616     },
59617
59618         //private
59619     onViewChange : function(){
59620         this.clearSelections(true);
59621     },
59622
59623         /**
59624          * Returns the currently selected cell,.
59625          * @return {Array} The selected cell (row, column) or null if none selected.
59626          */
59627     getSelectedCell : function(){
59628         return this.selection ? this.selection.cell : null;
59629     },
59630
59631     /**
59632      * Clears all selections.
59633      * @param {Boolean} true to prevent the gridview from being notified about the change.
59634      */
59635     clearSelections : function(preventNotify){
59636         var s = this.selection;
59637         if(s){
59638             if(preventNotify !== true){
59639                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59640             }
59641             this.selection = null;
59642             this.fireEvent("selectionchange", this, null);
59643         }
59644     },
59645
59646     /**
59647      * Returns true if there is a selection.
59648      * @return {Boolean}
59649      */
59650     hasSelection : function(){
59651         return this.selection ? true : false;
59652     },
59653
59654     /** @ignore */
59655     handleMouseDown : function(e, t){
59656         var v = this.grid.getView();
59657         if(this.isLocked()){
59658             return;
59659         };
59660         var row = v.findRowIndex(t);
59661         var cell = v.findCellIndex(t);
59662         if(row !== false && cell !== false){
59663             this.select(row, cell);
59664         }
59665     },
59666
59667     /**
59668      * Selects a cell.
59669      * @param {Number} rowIndex
59670      * @param {Number} collIndex
59671      */
59672     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59673         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59674             this.clearSelections();
59675             r = r || this.grid.dataSource.getAt(rowIndex);
59676             this.selection = {
59677                 record : r,
59678                 cell : [rowIndex, colIndex]
59679             };
59680             if(!preventViewNotify){
59681                 var v = this.grid.getView();
59682                 v.onCellSelect(rowIndex, colIndex);
59683                 if(preventFocus !== true){
59684                     v.focusCell(rowIndex, colIndex);
59685                 }
59686             }
59687             this.fireEvent("cellselect", this, rowIndex, colIndex);
59688             this.fireEvent("selectionchange", this, this.selection);
59689         }
59690     },
59691
59692         //private
59693     isSelectable : function(rowIndex, colIndex, cm){
59694         return !cm.isHidden(colIndex);
59695     },
59696
59697     /** @ignore */
59698     handleKeyDown : function(e){
59699         //Roo.log('Cell Sel Model handleKeyDown');
59700         if(!e.isNavKeyPress()){
59701             return;
59702         }
59703         var g = this.grid, s = this.selection;
59704         if(!s){
59705             e.stopEvent();
59706             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59707             if(cell){
59708                 this.select(cell[0], cell[1]);
59709             }
59710             return;
59711         }
59712         var sm = this;
59713         var walk = function(row, col, step){
59714             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59715         };
59716         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59717         var newCell;
59718
59719       
59720
59721         switch(k){
59722             case e.TAB:
59723                 // handled by onEditorKey
59724                 if (g.isEditor && g.editing) {
59725                     return;
59726                 }
59727                 if(e.shiftKey) {
59728                     newCell = walk(r, c-1, -1);
59729                 } else {
59730                     newCell = walk(r, c+1, 1);
59731                 }
59732                 break;
59733             
59734             case e.DOWN:
59735                newCell = walk(r+1, c, 1);
59736                 break;
59737             
59738             case e.UP:
59739                 newCell = walk(r-1, c, -1);
59740                 break;
59741             
59742             case e.RIGHT:
59743                 newCell = walk(r, c+1, 1);
59744                 break;
59745             
59746             case e.LEFT:
59747                 newCell = walk(r, c-1, -1);
59748                 break;
59749             
59750             case e.ENTER:
59751                 
59752                 if(g.isEditor && !g.editing){
59753                    g.startEditing(r, c);
59754                    e.stopEvent();
59755                    return;
59756                 }
59757                 
59758                 
59759              break;
59760         };
59761         if(newCell){
59762             this.select(newCell[0], newCell[1]);
59763             e.stopEvent();
59764             
59765         }
59766     },
59767
59768     acceptsNav : function(row, col, cm){
59769         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59770     },
59771     /**
59772      * Selects a cell.
59773      * @param {Number} field (not used) - as it's normally used as a listener
59774      * @param {Number} e - event - fake it by using
59775      *
59776      * var e = Roo.EventObjectImpl.prototype;
59777      * e.keyCode = e.TAB
59778      *
59779      * 
59780      */
59781     onEditorKey : function(field, e){
59782         
59783         var k = e.getKey(),
59784             newCell,
59785             g = this.grid,
59786             ed = g.activeEditor,
59787             forward = false;
59788         ///Roo.log('onEditorKey' + k);
59789         
59790         
59791         if (this.enter_is_tab && k == e.ENTER) {
59792             k = e.TAB;
59793         }
59794         
59795         if(k == e.TAB){
59796             if(e.shiftKey){
59797                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59798             }else{
59799                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59800                 forward = true;
59801             }
59802             
59803             e.stopEvent();
59804             
59805         } else if(k == e.ENTER &&  !e.ctrlKey){
59806             ed.completeEdit();
59807             e.stopEvent();
59808             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59809         
59810                 } else if(k == e.ESC){
59811             ed.cancelEdit();
59812         }
59813                 
59814         if (newCell) {
59815             var ecall = { cell : newCell, forward : forward };
59816             this.fireEvent('beforeeditnext', ecall );
59817             newCell = ecall.cell;
59818                         forward = ecall.forward;
59819         }
59820                 
59821         if(newCell){
59822             //Roo.log('next cell after edit');
59823             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59824         } else if (forward) {
59825             // tabbed past last
59826             this.fireEvent.defer(100, this, ['tabend',this]);
59827         }
59828     }
59829 });/*
59830  * Based on:
59831  * Ext JS Library 1.1.1
59832  * Copyright(c) 2006-2007, Ext JS, LLC.
59833  *
59834  * Originally Released Under LGPL - original licence link has changed is not relivant.
59835  *
59836  * Fork - LGPL
59837  * <script type="text/javascript">
59838  */
59839  
59840 /**
59841  * @class Roo.grid.EditorGrid
59842  * @extends Roo.grid.Grid
59843  * Class for creating and editable grid.
59844  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59845  * The container MUST have some type of size defined for the grid to fill. The container will be 
59846  * automatically set to position relative if it isn't already.
59847  * @param {Object} dataSource The data model to bind to
59848  * @param {Object} colModel The column model with info about this grid's columns
59849  */
59850 Roo.grid.EditorGrid = function(container, config){
59851     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59852     this.getGridEl().addClass("xedit-grid");
59853
59854     if(!this.selModel){
59855         this.selModel = new Roo.grid.CellSelectionModel();
59856     }
59857
59858     this.activeEditor = null;
59859
59860         this.addEvents({
59861             /**
59862              * @event beforeedit
59863              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59864              * <ul style="padding:5px;padding-left:16px;">
59865              * <li>grid - This grid</li>
59866              * <li>record - The record being edited</li>
59867              * <li>field - The field name being edited</li>
59868              * <li>value - The value for the field being edited.</li>
59869              * <li>row - The grid row index</li>
59870              * <li>column - The grid column index</li>
59871              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59872              * </ul>
59873              * @param {Object} e An edit event (see above for description)
59874              */
59875             "beforeedit" : true,
59876             /**
59877              * @event afteredit
59878              * Fires after a cell is edited. <br />
59879              * <ul style="padding:5px;padding-left:16px;">
59880              * <li>grid - This grid</li>
59881              * <li>record - The record being edited</li>
59882              * <li>field - The field name being edited</li>
59883              * <li>value - The value being set</li>
59884              * <li>originalValue - The original value for the field, before the edit.</li>
59885              * <li>row - The grid row index</li>
59886              * <li>column - The grid column index</li>
59887              * </ul>
59888              * @param {Object} e An edit event (see above for description)
59889              */
59890             "afteredit" : true,
59891             /**
59892              * @event validateedit
59893              * Fires after a cell is edited, but before the value is set in the record. 
59894          * You can use this to modify the value being set in the field, Return false
59895              * to cancel the change. The edit event object has the following properties <br />
59896              * <ul style="padding:5px;padding-left:16px;">
59897          * <li>editor - This editor</li>
59898              * <li>grid - This grid</li>
59899              * <li>record - The record being edited</li>
59900              * <li>field - The field name being edited</li>
59901              * <li>value - The value being set</li>
59902              * <li>originalValue - The original value for the field, before the edit.</li>
59903              * <li>row - The grid row index</li>
59904              * <li>column - The grid column index</li>
59905              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59906              * </ul>
59907              * @param {Object} e An edit event (see above for description)
59908              */
59909             "validateedit" : true
59910         });
59911     this.on("bodyscroll", this.stopEditing,  this);
59912     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59913 };
59914
59915 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59916     /**
59917      * @cfg {Number} clicksToEdit
59918      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59919      */
59920     clicksToEdit: 2,
59921
59922     // private
59923     isEditor : true,
59924     // private
59925     trackMouseOver: false, // causes very odd FF errors
59926
59927     onCellDblClick : function(g, row, col){
59928         this.startEditing(row, col);
59929     },
59930
59931     onEditComplete : function(ed, value, startValue){
59932         this.editing = false;
59933         this.activeEditor = null;
59934         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59935         var r = ed.record;
59936         var field = this.colModel.getDataIndex(ed.col);
59937         var e = {
59938             grid: this,
59939             record: r,
59940             field: field,
59941             originalValue: startValue,
59942             value: value,
59943             row: ed.row,
59944             column: ed.col,
59945             cancel:false,
59946             editor: ed
59947         };
59948         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59949         cell.show();
59950           
59951         if(String(value) !== String(startValue)){
59952             
59953             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59954                 r.set(field, e.value);
59955                 // if we are dealing with a combo box..
59956                 // then we also set the 'name' colum to be the displayField
59957                 if (ed.field.displayField && ed.field.name) {
59958                     r.set(ed.field.name, ed.field.el.dom.value);
59959                 }
59960                 
59961                 delete e.cancel; //?? why!!!
59962                 this.fireEvent("afteredit", e);
59963             }
59964         } else {
59965             this.fireEvent("afteredit", e); // always fire it!
59966         }
59967         this.view.focusCell(ed.row, ed.col);
59968     },
59969
59970     /**
59971      * Starts editing the specified for the specified row/column
59972      * @param {Number} rowIndex
59973      * @param {Number} colIndex
59974      */
59975     startEditing : function(row, col){
59976         this.stopEditing();
59977         if(this.colModel.isCellEditable(col, row)){
59978             this.view.ensureVisible(row, col, true);
59979           
59980             var r = this.dataSource.getAt(row);
59981             var field = this.colModel.getDataIndex(col);
59982             var cell = Roo.get(this.view.getCell(row,col));
59983             var e = {
59984                 grid: this,
59985                 record: r,
59986                 field: field,
59987                 value: r.data[field],
59988                 row: row,
59989                 column: col,
59990                 cancel:false 
59991             };
59992             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59993                 this.editing = true;
59994                 var ed = this.colModel.getCellEditor(col, row);
59995                 
59996                 if (!ed) {
59997                     return;
59998                 }
59999                 if(!ed.rendered){
60000                     ed.render(ed.parentEl || document.body);
60001                 }
60002                 ed.field.reset();
60003                
60004                 cell.hide();
60005                 
60006                 (function(){ // complex but required for focus issues in safari, ie and opera
60007                     ed.row = row;
60008                     ed.col = col;
60009                     ed.record = r;
60010                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60011                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60012                     this.activeEditor = ed;
60013                     var v = r.data[field];
60014                     ed.startEdit(this.view.getCell(row, col), v);
60015                     // combo's with 'displayField and name set
60016                     if (ed.field.displayField && ed.field.name) {
60017                         ed.field.el.dom.value = r.data[ed.field.name];
60018                     }
60019                     
60020                     
60021                 }).defer(50, this);
60022             }
60023         }
60024     },
60025         
60026     /**
60027      * Stops any active editing
60028      */
60029     stopEditing : function(){
60030         if(this.activeEditor){
60031             this.activeEditor.completeEdit();
60032         }
60033         this.activeEditor = null;
60034     },
60035         
60036          /**
60037      * Called to get grid's drag proxy text, by default returns this.ddText.
60038      * @return {String}
60039      */
60040     getDragDropText : function(){
60041         var count = this.selModel.getSelectedCell() ? 1 : 0;
60042         return String.format(this.ddText, count, count == 1 ? '' : 's');
60043     }
60044         
60045 });/*
60046  * Based on:
60047  * Ext JS Library 1.1.1
60048  * Copyright(c) 2006-2007, Ext JS, LLC.
60049  *
60050  * Originally Released Under LGPL - original licence link has changed is not relivant.
60051  *
60052  * Fork - LGPL
60053  * <script type="text/javascript">
60054  */
60055
60056 // private - not really -- you end up using it !
60057 // This is a support class used internally by the Grid components
60058
60059 /**
60060  * @class Roo.grid.GridEditor
60061  * @extends Roo.Editor
60062  * Class for creating and editable grid elements.
60063  * @param {Object} config any settings (must include field)
60064  */
60065 Roo.grid.GridEditor = function(field, config){
60066     if (!config && field.field) {
60067         config = field;
60068         field = Roo.factory(config.field, Roo.form);
60069     }
60070     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60071     field.monitorTab = false;
60072 };
60073
60074 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60075     
60076     /**
60077      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60078      */
60079     
60080     alignment: "tl-tl",
60081     autoSize: "width",
60082     hideEl : false,
60083     cls: "x-small-editor x-grid-editor",
60084     shim:false,
60085     shadow:"frame"
60086 });/*
60087  * Based on:
60088  * Ext JS Library 1.1.1
60089  * Copyright(c) 2006-2007, Ext JS, LLC.
60090  *
60091  * Originally Released Under LGPL - original licence link has changed is not relivant.
60092  *
60093  * Fork - LGPL
60094  * <script type="text/javascript">
60095  */
60096   
60097
60098   
60099 Roo.grid.PropertyRecord = Roo.data.Record.create([
60100     {name:'name',type:'string'},  'value'
60101 ]);
60102
60103
60104 Roo.grid.PropertyStore = function(grid, source){
60105     this.grid = grid;
60106     this.store = new Roo.data.Store({
60107         recordType : Roo.grid.PropertyRecord
60108     });
60109     this.store.on('update', this.onUpdate,  this);
60110     if(source){
60111         this.setSource(source);
60112     }
60113     Roo.grid.PropertyStore.superclass.constructor.call(this);
60114 };
60115
60116
60117
60118 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60119     setSource : function(o){
60120         this.source = o;
60121         this.store.removeAll();
60122         var data = [];
60123         for(var k in o){
60124             if(this.isEditableValue(o[k])){
60125                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60126             }
60127         }
60128         this.store.loadRecords({records: data}, {}, true);
60129     },
60130
60131     onUpdate : function(ds, record, type){
60132         if(type == Roo.data.Record.EDIT){
60133             var v = record.data['value'];
60134             var oldValue = record.modified['value'];
60135             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60136                 this.source[record.id] = v;
60137                 record.commit();
60138                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60139             }else{
60140                 record.reject();
60141             }
60142         }
60143     },
60144
60145     getProperty : function(row){
60146        return this.store.getAt(row);
60147     },
60148
60149     isEditableValue: function(val){
60150         if(val && val instanceof Date){
60151             return true;
60152         }else if(typeof val == 'object' || typeof val == 'function'){
60153             return false;
60154         }
60155         return true;
60156     },
60157
60158     setValue : function(prop, value){
60159         this.source[prop] = value;
60160         this.store.getById(prop).set('value', value);
60161     },
60162
60163     getSource : function(){
60164         return this.source;
60165     }
60166 });
60167
60168 Roo.grid.PropertyColumnModel = function(grid, store){
60169     this.grid = grid;
60170     var g = Roo.grid;
60171     g.PropertyColumnModel.superclass.constructor.call(this, [
60172         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60173         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60174     ]);
60175     this.store = store;
60176     this.bselect = Roo.DomHelper.append(document.body, {
60177         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60178             {tag: 'option', value: 'true', html: 'true'},
60179             {tag: 'option', value: 'false', html: 'false'}
60180         ]
60181     });
60182     Roo.id(this.bselect);
60183     var f = Roo.form;
60184     this.editors = {
60185         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60186         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60187         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60188         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60189         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60190     };
60191     this.renderCellDelegate = this.renderCell.createDelegate(this);
60192     this.renderPropDelegate = this.renderProp.createDelegate(this);
60193 };
60194
60195 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60196     
60197     
60198     nameText : 'Name',
60199     valueText : 'Value',
60200     
60201     dateFormat : 'm/j/Y',
60202     
60203     
60204     renderDate : function(dateVal){
60205         return dateVal.dateFormat(this.dateFormat);
60206     },
60207
60208     renderBool : function(bVal){
60209         return bVal ? 'true' : 'false';
60210     },
60211
60212     isCellEditable : function(colIndex, rowIndex){
60213         return colIndex == 1;
60214     },
60215
60216     getRenderer : function(col){
60217         return col == 1 ?
60218             this.renderCellDelegate : this.renderPropDelegate;
60219     },
60220
60221     renderProp : function(v){
60222         return this.getPropertyName(v);
60223     },
60224
60225     renderCell : function(val){
60226         var rv = val;
60227         if(val instanceof Date){
60228             rv = this.renderDate(val);
60229         }else if(typeof val == 'boolean'){
60230             rv = this.renderBool(val);
60231         }
60232         return Roo.util.Format.htmlEncode(rv);
60233     },
60234
60235     getPropertyName : function(name){
60236         var pn = this.grid.propertyNames;
60237         return pn && pn[name] ? pn[name] : name;
60238     },
60239
60240     getCellEditor : function(colIndex, rowIndex){
60241         var p = this.store.getProperty(rowIndex);
60242         var n = p.data['name'], val = p.data['value'];
60243         
60244         if(typeof(this.grid.customEditors[n]) == 'string'){
60245             return this.editors[this.grid.customEditors[n]];
60246         }
60247         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60248             return this.grid.customEditors[n];
60249         }
60250         if(val instanceof Date){
60251             return this.editors['date'];
60252         }else if(typeof val == 'number'){
60253             return this.editors['number'];
60254         }else if(typeof val == 'boolean'){
60255             return this.editors['boolean'];
60256         }else{
60257             return this.editors['string'];
60258         }
60259     }
60260 });
60261
60262 /**
60263  * @class Roo.grid.PropertyGrid
60264  * @extends Roo.grid.EditorGrid
60265  * This class represents the  interface of a component based property grid control.
60266  * <br><br>Usage:<pre><code>
60267  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60268       
60269  });
60270  // set any options
60271  grid.render();
60272  * </code></pre>
60273   
60274  * @constructor
60275  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60276  * The container MUST have some type of size defined for the grid to fill. The container will be
60277  * automatically set to position relative if it isn't already.
60278  * @param {Object} config A config object that sets properties on this grid.
60279  */
60280 Roo.grid.PropertyGrid = function(container, config){
60281     config = config || {};
60282     var store = new Roo.grid.PropertyStore(this);
60283     this.store = store;
60284     var cm = new Roo.grid.PropertyColumnModel(this, store);
60285     store.store.sort('name', 'ASC');
60286     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60287         ds: store.store,
60288         cm: cm,
60289         enableColLock:false,
60290         enableColumnMove:false,
60291         stripeRows:false,
60292         trackMouseOver: false,
60293         clicksToEdit:1
60294     }, config));
60295     this.getGridEl().addClass('x-props-grid');
60296     this.lastEditRow = null;
60297     this.on('columnresize', this.onColumnResize, this);
60298     this.addEvents({
60299          /**
60300              * @event beforepropertychange
60301              * Fires before a property changes (return false to stop?)
60302              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60303              * @param {String} id Record Id
60304              * @param {String} newval New Value
60305          * @param {String} oldval Old Value
60306              */
60307         "beforepropertychange": true,
60308         /**
60309              * @event propertychange
60310              * Fires after a property changes
60311              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60312              * @param {String} id Record Id
60313              * @param {String} newval New Value
60314          * @param {String} oldval Old Value
60315              */
60316         "propertychange": true
60317     });
60318     this.customEditors = this.customEditors || {};
60319 };
60320 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60321     
60322      /**
60323      * @cfg {Object} customEditors map of colnames=> custom editors.
60324      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60325      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60326      * false disables editing of the field.
60327          */
60328     
60329       /**
60330      * @cfg {Object} propertyNames map of property Names to their displayed value
60331          */
60332     
60333     render : function(){
60334         Roo.grid.PropertyGrid.superclass.render.call(this);
60335         this.autoSize.defer(100, this);
60336     },
60337
60338     autoSize : function(){
60339         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60340         if(this.view){
60341             this.view.fitColumns();
60342         }
60343     },
60344
60345     onColumnResize : function(){
60346         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60347         this.autoSize();
60348     },
60349     /**
60350      * Sets the data for the Grid
60351      * accepts a Key => Value object of all the elements avaiable.
60352      * @param {Object} data  to appear in grid.
60353      */
60354     setSource : function(source){
60355         this.store.setSource(source);
60356         //this.autoSize();
60357     },
60358     /**
60359      * Gets all the data from the grid.
60360      * @return {Object} data  data stored in grid
60361      */
60362     getSource : function(){
60363         return this.store.getSource();
60364     }
60365 });/*
60366   
60367  * Licence LGPL
60368  
60369  */
60370  
60371 /**
60372  * @class Roo.grid.Calendar
60373  * @extends Roo.grid.Grid
60374  * This class extends the Grid to provide a calendar widget
60375  * <br><br>Usage:<pre><code>
60376  var grid = new Roo.grid.Calendar("my-container-id", {
60377      ds: myDataStore,
60378      cm: myColModel,
60379      selModel: mySelectionModel,
60380      autoSizeColumns: true,
60381      monitorWindowResize: false,
60382      trackMouseOver: true
60383      eventstore : real data store..
60384  });
60385  // set any options
60386  grid.render();
60387   
60388   * @constructor
60389  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60390  * The container MUST have some type of size defined for the grid to fill. The container will be
60391  * automatically set to position relative if it isn't already.
60392  * @param {Object} config A config object that sets properties on this grid.
60393  */
60394 Roo.grid.Calendar = function(container, config){
60395         // initialize the container
60396         this.container = Roo.get(container);
60397         this.container.update("");
60398         this.container.setStyle("overflow", "hidden");
60399     this.container.addClass('x-grid-container');
60400
60401     this.id = this.container.id;
60402
60403     Roo.apply(this, config);
60404     // check and correct shorthanded configs
60405     
60406     var rows = [];
60407     var d =1;
60408     for (var r = 0;r < 6;r++) {
60409         
60410         rows[r]=[];
60411         for (var c =0;c < 7;c++) {
60412             rows[r][c]= '';
60413         }
60414     }
60415     if (this.eventStore) {
60416         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60417         this.eventStore.on('load',this.onLoad, this);
60418         this.eventStore.on('beforeload',this.clearEvents, this);
60419          
60420     }
60421     
60422     this.dataSource = new Roo.data.Store({
60423             proxy: new Roo.data.MemoryProxy(rows),
60424             reader: new Roo.data.ArrayReader({}, [
60425                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60426     });
60427
60428     this.dataSource.load();
60429     this.ds = this.dataSource;
60430     this.ds.xmodule = this.xmodule || false;
60431     
60432     
60433     var cellRender = function(v,x,r)
60434     {
60435         return String.format(
60436             '<div class="fc-day  fc-widget-content"><div>' +
60437                 '<div class="fc-event-container"></div>' +
60438                 '<div class="fc-day-number">{0}</div>'+
60439                 
60440                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60441             '</div></div>', v);
60442     
60443     }
60444     
60445     
60446     this.colModel = new Roo.grid.ColumnModel( [
60447         {
60448             xtype: 'ColumnModel',
60449             xns: Roo.grid,
60450             dataIndex : 'weekday0',
60451             header : 'Sunday',
60452             renderer : cellRender
60453         },
60454         {
60455             xtype: 'ColumnModel',
60456             xns: Roo.grid,
60457             dataIndex : 'weekday1',
60458             header : 'Monday',
60459             renderer : cellRender
60460         },
60461         {
60462             xtype: 'ColumnModel',
60463             xns: Roo.grid,
60464             dataIndex : 'weekday2',
60465             header : 'Tuesday',
60466             renderer : cellRender
60467         },
60468         {
60469             xtype: 'ColumnModel',
60470             xns: Roo.grid,
60471             dataIndex : 'weekday3',
60472             header : 'Wednesday',
60473             renderer : cellRender
60474         },
60475         {
60476             xtype: 'ColumnModel',
60477             xns: Roo.grid,
60478             dataIndex : 'weekday4',
60479             header : 'Thursday',
60480             renderer : cellRender
60481         },
60482         {
60483             xtype: 'ColumnModel',
60484             xns: Roo.grid,
60485             dataIndex : 'weekday5',
60486             header : 'Friday',
60487             renderer : cellRender
60488         },
60489         {
60490             xtype: 'ColumnModel',
60491             xns: Roo.grid,
60492             dataIndex : 'weekday6',
60493             header : 'Saturday',
60494             renderer : cellRender
60495         }
60496     ]);
60497     this.cm = this.colModel;
60498     this.cm.xmodule = this.xmodule || false;
60499  
60500         
60501           
60502     //this.selModel = new Roo.grid.CellSelectionModel();
60503     //this.sm = this.selModel;
60504     //this.selModel.init(this);
60505     
60506     
60507     if(this.width){
60508         this.container.setWidth(this.width);
60509     }
60510
60511     if(this.height){
60512         this.container.setHeight(this.height);
60513     }
60514     /** @private */
60515         this.addEvents({
60516         // raw events
60517         /**
60518          * @event click
60519          * The raw click event for the entire grid.
60520          * @param {Roo.EventObject} e
60521          */
60522         "click" : true,
60523         /**
60524          * @event dblclick
60525          * The raw dblclick event for the entire grid.
60526          * @param {Roo.EventObject} e
60527          */
60528         "dblclick" : true,
60529         /**
60530          * @event contextmenu
60531          * The raw contextmenu event for the entire grid.
60532          * @param {Roo.EventObject} e
60533          */
60534         "contextmenu" : true,
60535         /**
60536          * @event mousedown
60537          * The raw mousedown event for the entire grid.
60538          * @param {Roo.EventObject} e
60539          */
60540         "mousedown" : true,
60541         /**
60542          * @event mouseup
60543          * The raw mouseup event for the entire grid.
60544          * @param {Roo.EventObject} e
60545          */
60546         "mouseup" : true,
60547         /**
60548          * @event mouseover
60549          * The raw mouseover event for the entire grid.
60550          * @param {Roo.EventObject} e
60551          */
60552         "mouseover" : true,
60553         /**
60554          * @event mouseout
60555          * The raw mouseout event for the entire grid.
60556          * @param {Roo.EventObject} e
60557          */
60558         "mouseout" : true,
60559         /**
60560          * @event keypress
60561          * The raw keypress event for the entire grid.
60562          * @param {Roo.EventObject} e
60563          */
60564         "keypress" : true,
60565         /**
60566          * @event keydown
60567          * The raw keydown event for the entire grid.
60568          * @param {Roo.EventObject} e
60569          */
60570         "keydown" : true,
60571
60572         // custom events
60573
60574         /**
60575          * @event cellclick
60576          * Fires when a cell is clicked
60577          * @param {Grid} this
60578          * @param {Number} rowIndex
60579          * @param {Number} columnIndex
60580          * @param {Roo.EventObject} e
60581          */
60582         "cellclick" : true,
60583         /**
60584          * @event celldblclick
60585          * Fires when a cell is double clicked
60586          * @param {Grid} this
60587          * @param {Number} rowIndex
60588          * @param {Number} columnIndex
60589          * @param {Roo.EventObject} e
60590          */
60591         "celldblclick" : true,
60592         /**
60593          * @event rowclick
60594          * Fires when a row is clicked
60595          * @param {Grid} this
60596          * @param {Number} rowIndex
60597          * @param {Roo.EventObject} e
60598          */
60599         "rowclick" : true,
60600         /**
60601          * @event rowdblclick
60602          * Fires when a row is double clicked
60603          * @param {Grid} this
60604          * @param {Number} rowIndex
60605          * @param {Roo.EventObject} e
60606          */
60607         "rowdblclick" : true,
60608         /**
60609          * @event headerclick
60610          * Fires when a header is clicked
60611          * @param {Grid} this
60612          * @param {Number} columnIndex
60613          * @param {Roo.EventObject} e
60614          */
60615         "headerclick" : true,
60616         /**
60617          * @event headerdblclick
60618          * Fires when a header cell is double clicked
60619          * @param {Grid} this
60620          * @param {Number} columnIndex
60621          * @param {Roo.EventObject} e
60622          */
60623         "headerdblclick" : true,
60624         /**
60625          * @event rowcontextmenu
60626          * Fires when a row is right clicked
60627          * @param {Grid} this
60628          * @param {Number} rowIndex
60629          * @param {Roo.EventObject} e
60630          */
60631         "rowcontextmenu" : true,
60632         /**
60633          * @event cellcontextmenu
60634          * Fires when a cell is right clicked
60635          * @param {Grid} this
60636          * @param {Number} rowIndex
60637          * @param {Number} cellIndex
60638          * @param {Roo.EventObject} e
60639          */
60640          "cellcontextmenu" : true,
60641         /**
60642          * @event headercontextmenu
60643          * Fires when a header is right clicked
60644          * @param {Grid} this
60645          * @param {Number} columnIndex
60646          * @param {Roo.EventObject} e
60647          */
60648         "headercontextmenu" : true,
60649         /**
60650          * @event bodyscroll
60651          * Fires when the body element is scrolled
60652          * @param {Number} scrollLeft
60653          * @param {Number} scrollTop
60654          */
60655         "bodyscroll" : true,
60656         /**
60657          * @event columnresize
60658          * Fires when the user resizes a column
60659          * @param {Number} columnIndex
60660          * @param {Number} newSize
60661          */
60662         "columnresize" : true,
60663         /**
60664          * @event columnmove
60665          * Fires when the user moves a column
60666          * @param {Number} oldIndex
60667          * @param {Number} newIndex
60668          */
60669         "columnmove" : true,
60670         /**
60671          * @event startdrag
60672          * Fires when row(s) start being dragged
60673          * @param {Grid} this
60674          * @param {Roo.GridDD} dd The drag drop object
60675          * @param {event} e The raw browser event
60676          */
60677         "startdrag" : true,
60678         /**
60679          * @event enddrag
60680          * Fires when a drag operation is complete
60681          * @param {Grid} this
60682          * @param {Roo.GridDD} dd The drag drop object
60683          * @param {event} e The raw browser event
60684          */
60685         "enddrag" : true,
60686         /**
60687          * @event dragdrop
60688          * Fires when dragged row(s) are dropped on a valid DD target
60689          * @param {Grid} this
60690          * @param {Roo.GridDD} dd The drag drop object
60691          * @param {String} targetId The target drag drop object
60692          * @param {event} e The raw browser event
60693          */
60694         "dragdrop" : true,
60695         /**
60696          * @event dragover
60697          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60698          * @param {Grid} this
60699          * @param {Roo.GridDD} dd The drag drop object
60700          * @param {String} targetId The target drag drop object
60701          * @param {event} e The raw browser event
60702          */
60703         "dragover" : true,
60704         /**
60705          * @event dragenter
60706          *  Fires when the dragged row(s) first cross another DD target while being dragged
60707          * @param {Grid} this
60708          * @param {Roo.GridDD} dd The drag drop object
60709          * @param {String} targetId The target drag drop object
60710          * @param {event} e The raw browser event
60711          */
60712         "dragenter" : true,
60713         /**
60714          * @event dragout
60715          * Fires when the dragged row(s) leave another DD target while being dragged
60716          * @param {Grid} this
60717          * @param {Roo.GridDD} dd The drag drop object
60718          * @param {String} targetId The target drag drop object
60719          * @param {event} e The raw browser event
60720          */
60721         "dragout" : true,
60722         /**
60723          * @event rowclass
60724          * Fires when a row is rendered, so you can change add a style to it.
60725          * @param {GridView} gridview   The grid view
60726          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60727          */
60728         'rowclass' : true,
60729
60730         /**
60731          * @event render
60732          * Fires when the grid is rendered
60733          * @param {Grid} grid
60734          */
60735         'render' : true,
60736             /**
60737              * @event select
60738              * Fires when a date is selected
60739              * @param {DatePicker} this
60740              * @param {Date} date The selected date
60741              */
60742         'select': true,
60743         /**
60744              * @event monthchange
60745              * Fires when the displayed month changes 
60746              * @param {DatePicker} this
60747              * @param {Date} date The selected month
60748              */
60749         'monthchange': true,
60750         /**
60751              * @event evententer
60752              * Fires when mouse over an event
60753              * @param {Calendar} this
60754              * @param {event} Event
60755              */
60756         'evententer': true,
60757         /**
60758              * @event eventleave
60759              * Fires when the mouse leaves an
60760              * @param {Calendar} this
60761              * @param {event}
60762              */
60763         'eventleave': true,
60764         /**
60765              * @event eventclick
60766              * Fires when the mouse click an
60767              * @param {Calendar} this
60768              * @param {event}
60769              */
60770         'eventclick': true,
60771         /**
60772              * @event eventrender
60773              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60774              * @param {Calendar} this
60775              * @param {data} data to be modified
60776              */
60777         'eventrender': true
60778         
60779     });
60780
60781     Roo.grid.Grid.superclass.constructor.call(this);
60782     this.on('render', function() {
60783         this.view.el.addClass('x-grid-cal'); 
60784         
60785         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60786
60787     },this);
60788     
60789     if (!Roo.grid.Calendar.style) {
60790         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60791             
60792             
60793             '.x-grid-cal .x-grid-col' :  {
60794                 height: 'auto !important',
60795                 'vertical-align': 'top'
60796             },
60797             '.x-grid-cal  .fc-event-hori' : {
60798                 height: '14px'
60799             }
60800              
60801             
60802         }, Roo.id());
60803     }
60804
60805     
60806     
60807 };
60808 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60809     /**
60810      * @cfg {Store} eventStore The store that loads events.
60811      */
60812     eventStore : 25,
60813
60814      
60815     activeDate : false,
60816     startDay : 0,
60817     autoWidth : true,
60818     monitorWindowResize : false,
60819
60820     
60821     resizeColumns : function() {
60822         var col = (this.view.el.getWidth() / 7) - 3;
60823         // loop through cols, and setWidth
60824         for(var i =0 ; i < 7 ; i++){
60825             this.cm.setColumnWidth(i, col);
60826         }
60827     },
60828      setDate :function(date) {
60829         
60830         Roo.log('setDate?');
60831         
60832         this.resizeColumns();
60833         var vd = this.activeDate;
60834         this.activeDate = date;
60835 //        if(vd && this.el){
60836 //            var t = date.getTime();
60837 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60838 //                Roo.log('using add remove');
60839 //                
60840 //                this.fireEvent('monthchange', this, date);
60841 //                
60842 //                this.cells.removeClass("fc-state-highlight");
60843 //                this.cells.each(function(c){
60844 //                   if(c.dateValue == t){
60845 //                       c.addClass("fc-state-highlight");
60846 //                       setTimeout(function(){
60847 //                            try{c.dom.firstChild.focus();}catch(e){}
60848 //                       }, 50);
60849 //                       return false;
60850 //                   }
60851 //                   return true;
60852 //                });
60853 //                return;
60854 //            }
60855 //        }
60856         
60857         var days = date.getDaysInMonth();
60858         
60859         var firstOfMonth = date.getFirstDateOfMonth();
60860         var startingPos = firstOfMonth.getDay()-this.startDay;
60861         
60862         if(startingPos < this.startDay){
60863             startingPos += 7;
60864         }
60865         
60866         var pm = date.add(Date.MONTH, -1);
60867         var prevStart = pm.getDaysInMonth()-startingPos;
60868 //        
60869         
60870         
60871         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60872         
60873         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60874         //this.cells.addClassOnOver('fc-state-hover');
60875         
60876         var cells = this.cells.elements;
60877         var textEls = this.textNodes;
60878         
60879         //Roo.each(cells, function(cell){
60880         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60881         //});
60882         
60883         days += startingPos;
60884
60885         // convert everything to numbers so it's fast
60886         var day = 86400000;
60887         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60888         //Roo.log(d);
60889         //Roo.log(pm);
60890         //Roo.log(prevStart);
60891         
60892         var today = new Date().clearTime().getTime();
60893         var sel = date.clearTime().getTime();
60894         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60895         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60896         var ddMatch = this.disabledDatesRE;
60897         var ddText = this.disabledDatesText;
60898         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60899         var ddaysText = this.disabledDaysText;
60900         var format = this.format;
60901         
60902         var setCellClass = function(cal, cell){
60903             
60904             //Roo.log('set Cell Class');
60905             cell.title = "";
60906             var t = d.getTime();
60907             
60908             //Roo.log(d);
60909             
60910             
60911             cell.dateValue = t;
60912             if(t == today){
60913                 cell.className += " fc-today";
60914                 cell.className += " fc-state-highlight";
60915                 cell.title = cal.todayText;
60916             }
60917             if(t == sel){
60918                 // disable highlight in other month..
60919                 cell.className += " fc-state-highlight";
60920                 
60921             }
60922             // disabling
60923             if(t < min) {
60924                 //cell.className = " fc-state-disabled";
60925                 cell.title = cal.minText;
60926                 return;
60927             }
60928             if(t > max) {
60929                 //cell.className = " fc-state-disabled";
60930                 cell.title = cal.maxText;
60931                 return;
60932             }
60933             if(ddays){
60934                 if(ddays.indexOf(d.getDay()) != -1){
60935                     // cell.title = ddaysText;
60936                    // cell.className = " fc-state-disabled";
60937                 }
60938             }
60939             if(ddMatch && format){
60940                 var fvalue = d.dateFormat(format);
60941                 if(ddMatch.test(fvalue)){
60942                     cell.title = ddText.replace("%0", fvalue);
60943                    cell.className = " fc-state-disabled";
60944                 }
60945             }
60946             
60947             if (!cell.initialClassName) {
60948                 cell.initialClassName = cell.dom.className;
60949             }
60950             
60951             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60952         };
60953
60954         var i = 0;
60955         
60956         for(; i < startingPos; i++) {
60957             cells[i].dayName =  (++prevStart);
60958             Roo.log(textEls[i]);
60959             d.setDate(d.getDate()+1);
60960             
60961             //cells[i].className = "fc-past fc-other-month";
60962             setCellClass(this, cells[i]);
60963         }
60964         
60965         var intDay = 0;
60966         
60967         for(; i < days; i++){
60968             intDay = i - startingPos + 1;
60969             cells[i].dayName =  (intDay);
60970             d.setDate(d.getDate()+1);
60971             
60972             cells[i].className = ''; // "x-date-active";
60973             setCellClass(this, cells[i]);
60974         }
60975         var extraDays = 0;
60976         
60977         for(; i < 42; i++) {
60978             //textEls[i].innerHTML = (++extraDays);
60979             
60980             d.setDate(d.getDate()+1);
60981             cells[i].dayName = (++extraDays);
60982             cells[i].className = "fc-future fc-other-month";
60983             setCellClass(this, cells[i]);
60984         }
60985         
60986         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60987         
60988         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60989         
60990         // this will cause all the cells to mis
60991         var rows= [];
60992         var i =0;
60993         for (var r = 0;r < 6;r++) {
60994             for (var c =0;c < 7;c++) {
60995                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60996             }    
60997         }
60998         
60999         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61000         for(i=0;i<cells.length;i++) {
61001             
61002             this.cells.elements[i].dayName = cells[i].dayName ;
61003             this.cells.elements[i].className = cells[i].className;
61004             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61005             this.cells.elements[i].title = cells[i].title ;
61006             this.cells.elements[i].dateValue = cells[i].dateValue ;
61007         }
61008         
61009         
61010         
61011         
61012         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61013         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61014         
61015         ////if(totalRows != 6){
61016             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61017            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61018        // }
61019         
61020         this.fireEvent('monthchange', this, date);
61021         
61022         
61023     },
61024  /**
61025      * Returns the grid's SelectionModel.
61026      * @return {SelectionModel}
61027      */
61028     getSelectionModel : function(){
61029         if(!this.selModel){
61030             this.selModel = new Roo.grid.CellSelectionModel();
61031         }
61032         return this.selModel;
61033     },
61034
61035     load: function() {
61036         this.eventStore.load()
61037         
61038         
61039         
61040     },
61041     
61042     findCell : function(dt) {
61043         dt = dt.clearTime().getTime();
61044         var ret = false;
61045         this.cells.each(function(c){
61046             //Roo.log("check " +c.dateValue + '?=' + dt);
61047             if(c.dateValue == dt){
61048                 ret = c;
61049                 return false;
61050             }
61051             return true;
61052         });
61053         
61054         return ret;
61055     },
61056     
61057     findCells : function(rec) {
61058         var s = rec.data.start_dt.clone().clearTime().getTime();
61059        // Roo.log(s);
61060         var e= rec.data.end_dt.clone().clearTime().getTime();
61061        // Roo.log(e);
61062         var ret = [];
61063         this.cells.each(function(c){
61064              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61065             
61066             if(c.dateValue > e){
61067                 return ;
61068             }
61069             if(c.dateValue < s){
61070                 return ;
61071             }
61072             ret.push(c);
61073         });
61074         
61075         return ret;    
61076     },
61077     
61078     findBestRow: function(cells)
61079     {
61080         var ret = 0;
61081         
61082         for (var i =0 ; i < cells.length;i++) {
61083             ret  = Math.max(cells[i].rows || 0,ret);
61084         }
61085         return ret;
61086         
61087     },
61088     
61089     
61090     addItem : function(rec)
61091     {
61092         // look for vertical location slot in
61093         var cells = this.findCells(rec);
61094         
61095         rec.row = this.findBestRow(cells);
61096         
61097         // work out the location.
61098         
61099         var crow = false;
61100         var rows = [];
61101         for(var i =0; i < cells.length; i++) {
61102             if (!crow) {
61103                 crow = {
61104                     start : cells[i],
61105                     end :  cells[i]
61106                 };
61107                 continue;
61108             }
61109             if (crow.start.getY() == cells[i].getY()) {
61110                 // on same row.
61111                 crow.end = cells[i];
61112                 continue;
61113             }
61114             // different row.
61115             rows.push(crow);
61116             crow = {
61117                 start: cells[i],
61118                 end : cells[i]
61119             };
61120             
61121         }
61122         
61123         rows.push(crow);
61124         rec.els = [];
61125         rec.rows = rows;
61126         rec.cells = cells;
61127         for (var i = 0; i < cells.length;i++) {
61128             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61129             
61130         }
61131         
61132         
61133     },
61134     
61135     clearEvents: function() {
61136         
61137         if (!this.eventStore.getCount()) {
61138             return;
61139         }
61140         // reset number of rows in cells.
61141         Roo.each(this.cells.elements, function(c){
61142             c.rows = 0;
61143         });
61144         
61145         this.eventStore.each(function(e) {
61146             this.clearEvent(e);
61147         },this);
61148         
61149     },
61150     
61151     clearEvent : function(ev)
61152     {
61153         if (ev.els) {
61154             Roo.each(ev.els, function(el) {
61155                 el.un('mouseenter' ,this.onEventEnter, this);
61156                 el.un('mouseleave' ,this.onEventLeave, this);
61157                 el.remove();
61158             },this);
61159             ev.els = [];
61160         }
61161     },
61162     
61163     
61164     renderEvent : function(ev,ctr) {
61165         if (!ctr) {
61166              ctr = this.view.el.select('.fc-event-container',true).first();
61167         }
61168         
61169          
61170         this.clearEvent(ev);
61171             //code
61172        
61173         
61174         
61175         ev.els = [];
61176         var cells = ev.cells;
61177         var rows = ev.rows;
61178         this.fireEvent('eventrender', this, ev);
61179         
61180         for(var i =0; i < rows.length; i++) {
61181             
61182             cls = '';
61183             if (i == 0) {
61184                 cls += ' fc-event-start';
61185             }
61186             if ((i+1) == rows.length) {
61187                 cls += ' fc-event-end';
61188             }
61189             
61190             //Roo.log(ev.data);
61191             // how many rows should it span..
61192             var cg = this.eventTmpl.append(ctr,Roo.apply({
61193                 fccls : cls
61194                 
61195             }, ev.data) , true);
61196             
61197             
61198             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61199             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61200             cg.on('click', this.onEventClick, this, ev);
61201             
61202             ev.els.push(cg);
61203             
61204             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61205             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61206             //Roo.log(cg);
61207              
61208             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61209             cg.setWidth(ebox.right - sbox.x -2);
61210         }
61211     },
61212     
61213     renderEvents: function()
61214     {   
61215         // first make sure there is enough space..
61216         
61217         if (!this.eventTmpl) {
61218             this.eventTmpl = new Roo.Template(
61219                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61220                     '<div class="fc-event-inner">' +
61221                         '<span class="fc-event-time">{time}</span>' +
61222                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61223                     '</div>' +
61224                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61225                 '</div>'
61226             );
61227                 
61228         }
61229                
61230         
61231         
61232         this.cells.each(function(c) {
61233             //Roo.log(c.select('.fc-day-content div',true).first());
61234             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61235         });
61236         
61237         var ctr = this.view.el.select('.fc-event-container',true).first();
61238         
61239         var cls;
61240         this.eventStore.each(function(ev){
61241             
61242             this.renderEvent(ev);
61243              
61244              
61245         }, this);
61246         this.view.layout();
61247         
61248     },
61249     
61250     onEventEnter: function (e, el,event,d) {
61251         this.fireEvent('evententer', this, el, event);
61252     },
61253     
61254     onEventLeave: function (e, el,event,d) {
61255         this.fireEvent('eventleave', this, el, event);
61256     },
61257     
61258     onEventClick: function (e, el,event,d) {
61259         this.fireEvent('eventclick', this, el, event);
61260     },
61261     
61262     onMonthChange: function () {
61263         this.store.load();
61264     },
61265     
61266     onLoad: function () {
61267         
61268         //Roo.log('calendar onload');
61269 //         
61270         if(this.eventStore.getCount() > 0){
61271             
61272            
61273             
61274             this.eventStore.each(function(d){
61275                 
61276                 
61277                 // FIXME..
61278                 var add =   d.data;
61279                 if (typeof(add.end_dt) == 'undefined')  {
61280                     Roo.log("Missing End time in calendar data: ");
61281                     Roo.log(d);
61282                     return;
61283                 }
61284                 if (typeof(add.start_dt) == 'undefined')  {
61285                     Roo.log("Missing Start time in calendar data: ");
61286                     Roo.log(d);
61287                     return;
61288                 }
61289                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61290                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61291                 add.id = add.id || d.id;
61292                 add.title = add.title || '??';
61293                 
61294                 this.addItem(d);
61295                 
61296              
61297             },this);
61298         }
61299         
61300         this.renderEvents();
61301     }
61302     
61303
61304 });
61305 /*
61306  grid : {
61307                 xtype: 'Grid',
61308                 xns: Roo.grid,
61309                 listeners : {
61310                     render : function ()
61311                     {
61312                         _this.grid = this;
61313                         
61314                         if (!this.view.el.hasClass('course-timesheet')) {
61315                             this.view.el.addClass('course-timesheet');
61316                         }
61317                         if (this.tsStyle) {
61318                             this.ds.load({});
61319                             return; 
61320                         }
61321                         Roo.log('width');
61322                         Roo.log(_this.grid.view.el.getWidth());
61323                         
61324                         
61325                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61326                             '.course-timesheet .x-grid-row' : {
61327                                 height: '80px'
61328                             },
61329                             '.x-grid-row td' : {
61330                                 'vertical-align' : 0
61331                             },
61332                             '.course-edit-link' : {
61333                                 'color' : 'blue',
61334                                 'text-overflow' : 'ellipsis',
61335                                 'overflow' : 'hidden',
61336                                 'white-space' : 'nowrap',
61337                                 'cursor' : 'pointer'
61338                             },
61339                             '.sub-link' : {
61340                                 'color' : 'green'
61341                             },
61342                             '.de-act-sup-link' : {
61343                                 'color' : 'purple',
61344                                 'text-decoration' : 'line-through'
61345                             },
61346                             '.de-act-link' : {
61347                                 'color' : 'red',
61348                                 'text-decoration' : 'line-through'
61349                             },
61350                             '.course-timesheet .course-highlight' : {
61351                                 'border-top-style': 'dashed !important',
61352                                 'border-bottom-bottom': 'dashed !important'
61353                             },
61354                             '.course-timesheet .course-item' : {
61355                                 'font-family'   : 'tahoma, arial, helvetica',
61356                                 'font-size'     : '11px',
61357                                 'overflow'      : 'hidden',
61358                                 'padding-left'  : '10px',
61359                                 'padding-right' : '10px',
61360                                 'padding-top' : '10px' 
61361                             }
61362                             
61363                         }, Roo.id());
61364                                 this.ds.load({});
61365                     }
61366                 },
61367                 autoWidth : true,
61368                 monitorWindowResize : false,
61369                 cellrenderer : function(v,x,r)
61370                 {
61371                     return v;
61372                 },
61373                 sm : {
61374                     xtype: 'CellSelectionModel',
61375                     xns: Roo.grid
61376                 },
61377                 dataSource : {
61378                     xtype: 'Store',
61379                     xns: Roo.data,
61380                     listeners : {
61381                         beforeload : function (_self, options)
61382                         {
61383                             options.params = options.params || {};
61384                             options.params._month = _this.monthField.getValue();
61385                             options.params.limit = 9999;
61386                             options.params['sort'] = 'when_dt';    
61387                             options.params['dir'] = 'ASC';    
61388                             this.proxy.loadResponse = this.loadResponse;
61389                             Roo.log("load?");
61390                             //this.addColumns();
61391                         },
61392                         load : function (_self, records, options)
61393                         {
61394                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61395                                 // if you click on the translation.. you can edit it...
61396                                 var el = Roo.get(this);
61397                                 var id = el.dom.getAttribute('data-id');
61398                                 var d = el.dom.getAttribute('data-date');
61399                                 var t = el.dom.getAttribute('data-time');
61400                                 //var id = this.child('span').dom.textContent;
61401                                 
61402                                 //Roo.log(this);
61403                                 Pman.Dialog.CourseCalendar.show({
61404                                     id : id,
61405                                     when_d : d,
61406                                     when_t : t,
61407                                     productitem_active : id ? 1 : 0
61408                                 }, function() {
61409                                     _this.grid.ds.load({});
61410                                 });
61411                            
61412                            });
61413                            
61414                            _this.panel.fireEvent('resize', [ '', '' ]);
61415                         }
61416                     },
61417                     loadResponse : function(o, success, response){
61418                             // this is overridden on before load..
61419                             
61420                             Roo.log("our code?");       
61421                             //Roo.log(success);
61422                             //Roo.log(response)
61423                             delete this.activeRequest;
61424                             if(!success){
61425                                 this.fireEvent("loadexception", this, o, response);
61426                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61427                                 return;
61428                             }
61429                             var result;
61430                             try {
61431                                 result = o.reader.read(response);
61432                             }catch(e){
61433                                 Roo.log("load exception?");
61434                                 this.fireEvent("loadexception", this, o, response, e);
61435                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61436                                 return;
61437                             }
61438                             Roo.log("ready...");        
61439                             // loop through result.records;
61440                             // and set this.tdate[date] = [] << array of records..
61441                             _this.tdata  = {};
61442                             Roo.each(result.records, function(r){
61443                                 //Roo.log(r.data);
61444                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61445                                     _this.tdata[r.data.when_dt.format('j')] = [];
61446                                 }
61447                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61448                             });
61449                             
61450                             //Roo.log(_this.tdata);
61451                             
61452                             result.records = [];
61453                             result.totalRecords = 6;
61454                     
61455                             // let's generate some duumy records for the rows.
61456                             //var st = _this.dateField.getValue();
61457                             
61458                             // work out monday..
61459                             //st = st.add(Date.DAY, -1 * st.format('w'));
61460                             
61461                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61462                             
61463                             var firstOfMonth = date.getFirstDayOfMonth();
61464                             var days = date.getDaysInMonth();
61465                             var d = 1;
61466                             var firstAdded = false;
61467                             for (var i = 0; i < result.totalRecords ; i++) {
61468                                 //var d= st.add(Date.DAY, i);
61469                                 var row = {};
61470                                 var added = 0;
61471                                 for(var w = 0 ; w < 7 ; w++){
61472                                     if(!firstAdded && firstOfMonth != w){
61473                                         continue;
61474                                     }
61475                                     if(d > days){
61476                                         continue;
61477                                     }
61478                                     firstAdded = true;
61479                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61480                                     row['weekday'+w] = String.format(
61481                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61482                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61483                                                     d,
61484                                                     date.format('Y-m-')+dd
61485                                                 );
61486                                     added++;
61487                                     if(typeof(_this.tdata[d]) != 'undefined'){
61488                                         Roo.each(_this.tdata[d], function(r){
61489                                             var is_sub = '';
61490                                             var deactive = '';
61491                                             var id = r.id;
61492                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61493                                             if(r.parent_id*1>0){
61494                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61495                                                 id = r.parent_id;
61496                                             }
61497                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61498                                                 deactive = 'de-act-link';
61499                                             }
61500                                             
61501                                             row['weekday'+w] += String.format(
61502                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61503                                                     id, //0
61504                                                     r.product_id_name, //1
61505                                                     r.when_dt.format('h:ia'), //2
61506                                                     is_sub, //3
61507                                                     deactive, //4
61508                                                     desc // 5
61509                                             );
61510                                         });
61511                                     }
61512                                     d++;
61513                                 }
61514                                 
61515                                 // only do this if something added..
61516                                 if(added > 0){ 
61517                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61518                                 }
61519                                 
61520                                 
61521                                 // push it twice. (second one with an hour..
61522                                 
61523                             }
61524                             //Roo.log(result);
61525                             this.fireEvent("load", this, o, o.request.arg);
61526                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61527                         },
61528                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61529                     proxy : {
61530                         xtype: 'HttpProxy',
61531                         xns: Roo.data,
61532                         method : 'GET',
61533                         url : baseURL + '/Roo/Shop_course.php'
61534                     },
61535                     reader : {
61536                         xtype: 'JsonReader',
61537                         xns: Roo.data,
61538                         id : 'id',
61539                         fields : [
61540                             {
61541                                 'name': 'id',
61542                                 'type': 'int'
61543                             },
61544                             {
61545                                 'name': 'when_dt',
61546                                 'type': 'string'
61547                             },
61548                             {
61549                                 'name': 'end_dt',
61550                                 'type': 'string'
61551                             },
61552                             {
61553                                 'name': 'parent_id',
61554                                 'type': 'int'
61555                             },
61556                             {
61557                                 'name': 'product_id',
61558                                 'type': 'int'
61559                             },
61560                             {
61561                                 'name': 'productitem_id',
61562                                 'type': 'int'
61563                             },
61564                             {
61565                                 'name': 'guid',
61566                                 'type': 'int'
61567                             }
61568                         ]
61569                     }
61570                 },
61571                 toolbar : {
61572                     xtype: 'Toolbar',
61573                     xns: Roo,
61574                     items : [
61575                         {
61576                             xtype: 'Button',
61577                             xns: Roo.Toolbar,
61578                             listeners : {
61579                                 click : function (_self, e)
61580                                 {
61581                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61582                                     sd.setMonth(sd.getMonth()-1);
61583                                     _this.monthField.setValue(sd.format('Y-m-d'));
61584                                     _this.grid.ds.load({});
61585                                 }
61586                             },
61587                             text : "Back"
61588                         },
61589                         {
61590                             xtype: 'Separator',
61591                             xns: Roo.Toolbar
61592                         },
61593                         {
61594                             xtype: 'MonthField',
61595                             xns: Roo.form,
61596                             listeners : {
61597                                 render : function (_self)
61598                                 {
61599                                     _this.monthField = _self;
61600                                    // _this.monthField.set  today
61601                                 },
61602                                 select : function (combo, date)
61603                                 {
61604                                     _this.grid.ds.load({});
61605                                 }
61606                             },
61607                             value : (function() { return new Date(); })()
61608                         },
61609                         {
61610                             xtype: 'Separator',
61611                             xns: Roo.Toolbar
61612                         },
61613                         {
61614                             xtype: 'TextItem',
61615                             xns: Roo.Toolbar,
61616                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61617                         },
61618                         {
61619                             xtype: 'Fill',
61620                             xns: Roo.Toolbar
61621                         },
61622                         {
61623                             xtype: 'Button',
61624                             xns: Roo.Toolbar,
61625                             listeners : {
61626                                 click : function (_self, e)
61627                                 {
61628                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61629                                     sd.setMonth(sd.getMonth()+1);
61630                                     _this.monthField.setValue(sd.format('Y-m-d'));
61631                                     _this.grid.ds.load({});
61632                                 }
61633                             },
61634                             text : "Next"
61635                         }
61636                     ]
61637                 },
61638                  
61639             }
61640         };
61641         
61642         *//*
61643  * Based on:
61644  * Ext JS Library 1.1.1
61645  * Copyright(c) 2006-2007, Ext JS, LLC.
61646  *
61647  * Originally Released Under LGPL - original licence link has changed is not relivant.
61648  *
61649  * Fork - LGPL
61650  * <script type="text/javascript">
61651  */
61652  
61653 /**
61654  * @class Roo.LoadMask
61655  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61656  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61657  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61658  * element's UpdateManager load indicator and will be destroyed after the initial load.
61659  * @constructor
61660  * Create a new LoadMask
61661  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61662  * @param {Object} config The config object
61663  */
61664 Roo.LoadMask = function(el, config){
61665     this.el = Roo.get(el);
61666     Roo.apply(this, config);
61667     if(this.store){
61668         this.store.on('beforeload', this.onBeforeLoad, this);
61669         this.store.on('load', this.onLoad, this);
61670         this.store.on('loadexception', this.onLoadException, this);
61671         this.removeMask = false;
61672     }else{
61673         var um = this.el.getUpdateManager();
61674         um.showLoadIndicator = false; // disable the default indicator
61675         um.on('beforeupdate', this.onBeforeLoad, this);
61676         um.on('update', this.onLoad, this);
61677         um.on('failure', this.onLoad, this);
61678         this.removeMask = true;
61679     }
61680 };
61681
61682 Roo.LoadMask.prototype = {
61683     /**
61684      * @cfg {Boolean} removeMask
61685      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61686      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61687      */
61688     removeMask : false,
61689     /**
61690      * @cfg {String} msg
61691      * The text to display in a centered loading message box (defaults to 'Loading...')
61692      */
61693     msg : 'Loading...',
61694     /**
61695      * @cfg {String} msgCls
61696      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61697      */
61698     msgCls : 'x-mask-loading',
61699
61700     /**
61701      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61702      * @type Boolean
61703      */
61704     disabled: false,
61705
61706     /**
61707      * Disables the mask to prevent it from being displayed
61708      */
61709     disable : function(){
61710        this.disabled = true;
61711     },
61712
61713     /**
61714      * Enables the mask so that it can be displayed
61715      */
61716     enable : function(){
61717         this.disabled = false;
61718     },
61719     
61720     onLoadException : function()
61721     {
61722         Roo.log(arguments);
61723         
61724         if (typeof(arguments[3]) != 'undefined') {
61725             Roo.MessageBox.alert("Error loading",arguments[3]);
61726         } 
61727         /*
61728         try {
61729             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61730                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61731             }   
61732         } catch(e) {
61733             
61734         }
61735         */
61736     
61737         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61738     },
61739     // private
61740     onLoad : function()
61741     {
61742         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61743     },
61744
61745     // private
61746     onBeforeLoad : function(){
61747         if(!this.disabled){
61748             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61749         }
61750     },
61751
61752     // private
61753     destroy : function(){
61754         if(this.store){
61755             this.store.un('beforeload', this.onBeforeLoad, this);
61756             this.store.un('load', this.onLoad, this);
61757             this.store.un('loadexception', this.onLoadException, this);
61758         }else{
61759             var um = this.el.getUpdateManager();
61760             um.un('beforeupdate', this.onBeforeLoad, this);
61761             um.un('update', this.onLoad, this);
61762             um.un('failure', this.onLoad, this);
61763         }
61764     }
61765 };/*
61766  * Based on:
61767  * Ext JS Library 1.1.1
61768  * Copyright(c) 2006-2007, Ext JS, LLC.
61769  *
61770  * Originally Released Under LGPL - original licence link has changed is not relivant.
61771  *
61772  * Fork - LGPL
61773  * <script type="text/javascript">
61774  */
61775
61776
61777 /**
61778  * @class Roo.XTemplate
61779  * @extends Roo.Template
61780  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61781 <pre><code>
61782 var t = new Roo.XTemplate(
61783         '&lt;select name="{name}"&gt;',
61784                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61785         '&lt;/select&gt;'
61786 );
61787  
61788 // then append, applying the master template values
61789  </code></pre>
61790  *
61791  * Supported features:
61792  *
61793  *  Tags:
61794
61795 <pre><code>
61796       {a_variable} - output encoded.
61797       {a_variable.format:("Y-m-d")} - call a method on the variable
61798       {a_variable:raw} - unencoded output
61799       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61800       {a_variable:this.method_on_template(...)} - call a method on the template object.
61801  
61802 </code></pre>
61803  *  The tpl tag:
61804 <pre><code>
61805         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61806         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61807         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61808         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61809   
61810         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61811         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61812 </code></pre>
61813  *      
61814  */
61815 Roo.XTemplate = function()
61816 {
61817     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61818     if (this.html) {
61819         this.compile();
61820     }
61821 };
61822
61823
61824 Roo.extend(Roo.XTemplate, Roo.Template, {
61825
61826     /**
61827      * The various sub templates
61828      */
61829     tpls : false,
61830     /**
61831      *
61832      * basic tag replacing syntax
61833      * WORD:WORD()
61834      *
61835      * // you can fake an object call by doing this
61836      *  x.t:(test,tesT) 
61837      * 
61838      */
61839     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61840
61841     /**
61842      * compile the template
61843      *
61844      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61845      *
61846      */
61847     compile: function()
61848     {
61849         var s = this.html;
61850      
61851         s = ['<tpl>', s, '</tpl>'].join('');
61852     
61853         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61854             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61855             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61856             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61857             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61858             m,
61859             id     = 0,
61860             tpls   = [];
61861     
61862         while(true == !!(m = s.match(re))){
61863             var forMatch   = m[0].match(nameRe),
61864                 ifMatch   = m[0].match(ifRe),
61865                 execMatch   = m[0].match(execRe),
61866                 namedMatch   = m[0].match(namedRe),
61867                 
61868                 exp  = null, 
61869                 fn   = null,
61870                 exec = null,
61871                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61872                 
61873             if (ifMatch) {
61874                 // if - puts fn into test..
61875                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61876                 if(exp){
61877                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61878                 }
61879             }
61880             
61881             if (execMatch) {
61882                 // exec - calls a function... returns empty if true is  returned.
61883                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61884                 if(exp){
61885                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61886                 }
61887             }
61888             
61889             
61890             if (name) {
61891                 // for = 
61892                 switch(name){
61893                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61894                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61895                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61896                 }
61897             }
61898             var uid = namedMatch ? namedMatch[1] : id;
61899             
61900             
61901             tpls.push({
61902                 id:     namedMatch ? namedMatch[1] : id,
61903                 target: name,
61904                 exec:   exec,
61905                 test:   fn,
61906                 body:   m[1] || ''
61907             });
61908             if (namedMatch) {
61909                 s = s.replace(m[0], '');
61910             } else { 
61911                 s = s.replace(m[0], '{xtpl'+ id + '}');
61912             }
61913             ++id;
61914         }
61915         this.tpls = [];
61916         for(var i = tpls.length-1; i >= 0; --i){
61917             this.compileTpl(tpls[i]);
61918             this.tpls[tpls[i].id] = tpls[i];
61919         }
61920         this.master = tpls[tpls.length-1];
61921         return this;
61922     },
61923     /**
61924      * same as applyTemplate, except it's done to one of the subTemplates
61925      * when using named templates, you can do:
61926      *
61927      * var str = pl.applySubTemplate('your-name', values);
61928      *
61929      * 
61930      * @param {Number} id of the template
61931      * @param {Object} values to apply to template
61932      * @param {Object} parent (normaly the instance of this object)
61933      */
61934     applySubTemplate : function(id, values, parent)
61935     {
61936         
61937         
61938         var t = this.tpls[id];
61939         
61940         
61941         try { 
61942             if(t.test && !t.test.call(this, values, parent)){
61943                 return '';
61944             }
61945         } catch(e) {
61946             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61947             Roo.log(e.toString());
61948             Roo.log(t.test);
61949             return ''
61950         }
61951         try { 
61952             
61953             if(t.exec && t.exec.call(this, values, parent)){
61954                 return '';
61955             }
61956         } catch(e) {
61957             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61958             Roo.log(e.toString());
61959             Roo.log(t.exec);
61960             return ''
61961         }
61962         try {
61963             var vs = t.target ? t.target.call(this, values, parent) : values;
61964             parent = t.target ? values : parent;
61965             if(t.target && vs instanceof Array){
61966                 var buf = [];
61967                 for(var i = 0, len = vs.length; i < len; i++){
61968                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61969                 }
61970                 return buf.join('');
61971             }
61972             return t.compiled.call(this, vs, parent);
61973         } catch (e) {
61974             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61975             Roo.log(e.toString());
61976             Roo.log(t.compiled);
61977             return '';
61978         }
61979     },
61980
61981     compileTpl : function(tpl)
61982     {
61983         var fm = Roo.util.Format;
61984         var useF = this.disableFormats !== true;
61985         var sep = Roo.isGecko ? "+" : ",";
61986         var undef = function(str) {
61987             Roo.log("Property not found :"  + str);
61988             return '';
61989         };
61990         
61991         var fn = function(m, name, format, args)
61992         {
61993             //Roo.log(arguments);
61994             args = args ? args.replace(/\\'/g,"'") : args;
61995             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61996             if (typeof(format) == 'undefined') {
61997                 format= 'htmlEncode';
61998             }
61999             if (format == 'raw' ) {
62000                 format = false;
62001             }
62002             
62003             if(name.substr(0, 4) == 'xtpl'){
62004                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62005             }
62006             
62007             // build an array of options to determine if value is undefined..
62008             
62009             // basically get 'xxxx.yyyy' then do
62010             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62011             //    (function () { Roo.log("Property not found"); return ''; })() :
62012             //    ......
62013             
62014             var udef_ar = [];
62015             var lookfor = '';
62016             Roo.each(name.split('.'), function(st) {
62017                 lookfor += (lookfor.length ? '.': '') + st;
62018                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62019             });
62020             
62021             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62022             
62023             
62024             if(format && useF){
62025                 
62026                 args = args ? ',' + args : "";
62027                  
62028                 if(format.substr(0, 5) != "this."){
62029                     format = "fm." + format + '(';
62030                 }else{
62031                     format = 'this.call("'+ format.substr(5) + '", ';
62032                     args = ", values";
62033                 }
62034                 
62035                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62036             }
62037              
62038             if (args.length) {
62039                 // called with xxyx.yuu:(test,test)
62040                 // change to ()
62041                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62042             }
62043             // raw.. - :raw modifier..
62044             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62045             
62046         };
62047         var body;
62048         // branched to use + in gecko and [].join() in others
62049         if(Roo.isGecko){
62050             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62051                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62052                     "';};};";
62053         }else{
62054             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62055             body.push(tpl.body.replace(/(\r\n|\n)/g,
62056                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62057             body.push("'].join('');};};");
62058             body = body.join('');
62059         }
62060         
62061         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62062        
62063         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62064         eval(body);
62065         
62066         return this;
62067     },
62068
62069     applyTemplate : function(values){
62070         return this.master.compiled.call(this, values, {});
62071         //var s = this.subs;
62072     },
62073
62074     apply : function(){
62075         return this.applyTemplate.apply(this, arguments);
62076     }
62077
62078  });
62079
62080 Roo.XTemplate.from = function(el){
62081     el = Roo.getDom(el);
62082     return new Roo.XTemplate(el.value || el.innerHTML);
62083 };