roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052          }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059       
1060         // sort?? a.sort().equals(b.sort());
1061       
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064                 return false;
1065             }
1066         }
1067         return true;
1068     }
1069 });
1070
1071
1072  
1073 /*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Date
1086  *
1087  * The date parsing and format syntax is a subset of
1088  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1089  * supported will provide results equivalent to their PHP versions.
1090  *
1091  * Following is the list of all currently supported formats:
1092  *<pre>
1093 Sample date:
1094 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1095
1096 Format  Output      Description
1097 ------  ----------  --------------------------------------------------------------
1098   d      10         Day of the month, 2 digits with leading zeros
1099   D      Wed        A textual representation of a day, three letters
1100   j      10         Day of the month without leading zeros
1101   l      Wednesday  A full textual representation of the day of the week
1102   S      th         English ordinal day of month suffix, 2 chars (use with j)
1103   w      3          Numeric representation of the day of the week
1104   z      9          The julian date, or day of the year (0-365)
1105   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1106   F      January    A full textual representation of the month
1107   m      01         Numeric representation of a month, with leading zeros
1108   M      Jan        Month name abbreviation, three letters
1109   n      1          Numeric representation of a month, without leading zeros
1110   t      31         Number of days in the given month
1111   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1112   Y      2007       A full numeric representation of a year, 4 digits
1113   y      07         A two digit representation of a year
1114   a      pm         Lowercase Ante meridiem and Post meridiem
1115   A      PM         Uppercase Ante meridiem and Post meridiem
1116   g      3          12-hour format of an hour without leading zeros
1117   G      15         24-hour format of an hour without leading zeros
1118   h      03         12-hour format of an hour with leading zeros
1119   H      15         24-hour format of an hour with leading zeros
1120   i      05         Minutes with leading zeros
1121   s      01         Seconds, with leading zeros
1122   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1123   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1124   T      CST        Timezone setting of the machine running the code
1125   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1126 </pre>
1127  *
1128  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1129  * <pre><code>
1130 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1131 document.write(dt.format('Y-m-d'));                         //2007-01-10
1132 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1133 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1134  </code></pre>
1135  *
1136  * Here are some standard date/time patterns that you might find helpful.  They
1137  * are not part of the source of Date.js, but to use them you can simply copy this
1138  * block of code into any script that is included after Date.js and they will also become
1139  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1140  * <pre><code>
1141 Date.patterns = {
1142     ISO8601Long:"Y-m-d H:i:s",
1143     ISO8601Short:"Y-m-d",
1144     ShortDate: "n/j/Y",
1145     LongDate: "l, F d, Y",
1146     FullDateTime: "l, F d, Y g:i:s A",
1147     MonthDay: "F d",
1148     ShortTime: "g:i A",
1149     LongTime: "g:i:s A",
1150     SortableDateTime: "Y-m-d\\TH:i:s",
1151     UniversalSortableDateTime: "Y-m-d H:i:sO",
1152     YearMonth: "F, Y"
1153 };
1154 </code></pre>
1155  *
1156  * Example usage:
1157  * <pre><code>
1158 var dt = new Date();
1159 document.write(dt.format(Date.patterns.ShortDate));
1160  </code></pre>
1161  */
1162
1163 /*
1164  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1165  * They generate precompiled functions from date formats instead of parsing and
1166  * processing the pattern every time you format a date.  These functions are available
1167  * on every Date object (any javascript function).
1168  *
1169  * The original article and download are here:
1170  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1171  *
1172  */
1173  
1174  
1175  // was in core
1176 /**
1177  Returns the number of milliseconds between this date and date
1178  @param {Date} date (optional) Defaults to now
1179  @return {Number} The diff in milliseconds
1180  @member Date getElapsed
1181  */
1182 Date.prototype.getElapsed = function(date) {
1183         return Math.abs((date || new Date()).getTime()-this.getTime());
1184 };
1185 // was in date file..
1186
1187
1188 // private
1189 Date.parseFunctions = {count:0};
1190 // private
1191 Date.parseRegexes = [];
1192 // private
1193 Date.formatFunctions = {count:0};
1194
1195 // private
1196 Date.prototype.dateFormat = function(format) {
1197     if (Date.formatFunctions[format] == null) {
1198         Date.createNewFormat(format);
1199     }
1200     var func = Date.formatFunctions[format];
1201     return this[func]();
1202 };
1203
1204
1205 /**
1206  * Formats a date given the supplied format string
1207  * @param {String} format The format string
1208  * @return {String} The formatted date
1209  * @method
1210  */
1211 Date.prototype.format = Date.prototype.dateFormat;
1212
1213 // private
1214 Date.createNewFormat = function(format) {
1215     var funcName = "format" + Date.formatFunctions.count++;
1216     Date.formatFunctions[format] = funcName;
1217     var code = "Date.prototype." + funcName + " = function(){return ";
1218     var special = false;
1219     var ch = '';
1220     for (var i = 0; i < format.length; ++i) {
1221         ch = format.charAt(i);
1222         if (!special && ch == "\\") {
1223             special = true;
1224         }
1225         else if (special) {
1226             special = false;
1227             code += "'" + String.escape(ch) + "' + ";
1228         }
1229         else {
1230             code += Date.getFormatCode(ch);
1231         }
1232     }
1233     /** eval:var:zzzzzzzzzzzzz */
1234     eval(code.substring(0, code.length - 3) + ";}");
1235 };
1236
1237 // private
1238 Date.getFormatCode = function(character) {
1239     switch (character) {
1240     case "d":
1241         return "String.leftPad(this.getDate(), 2, '0') + ";
1242     case "D":
1243         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1244     case "j":
1245         return "this.getDate() + ";
1246     case "l":
1247         return "Date.dayNames[this.getDay()] + ";
1248     case "S":
1249         return "this.getSuffix() + ";
1250     case "w":
1251         return "this.getDay() + ";
1252     case "z":
1253         return "this.getDayOfYear() + ";
1254     case "W":
1255         return "this.getWeekOfYear() + ";
1256     case "F":
1257         return "Date.monthNames[this.getMonth()] + ";
1258     case "m":
1259         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1260     case "M":
1261         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1262     case "n":
1263         return "(this.getMonth() + 1) + ";
1264     case "t":
1265         return "this.getDaysInMonth() + ";
1266     case "L":
1267         return "(this.isLeapYear() ? 1 : 0) + ";
1268     case "Y":
1269         return "this.getFullYear() + ";
1270     case "y":
1271         return "('' + this.getFullYear()).substring(2, 4) + ";
1272     case "a":
1273         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1274     case "A":
1275         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1276     case "g":
1277         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1278     case "G":
1279         return "this.getHours() + ";
1280     case "h":
1281         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1282     case "H":
1283         return "String.leftPad(this.getHours(), 2, '0') + ";
1284     case "i":
1285         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1286     case "s":
1287         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1288     case "O":
1289         return "this.getGMTOffset() + ";
1290     case "P":
1291         return "this.getGMTColonOffset() + ";
1292     case "T":
1293         return "this.getTimezone() + ";
1294     case "Z":
1295         return "(this.getTimezoneOffset() * -60) + ";
1296     default:
1297         return "'" + String.escape(character) + "' + ";
1298     }
1299 };
1300
1301 /**
1302  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1303  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1304  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1305  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1306  * string or the parse operation will fail.
1307  * Example Usage:
1308 <pre><code>
1309 //dt = Fri May 25 2007 (current date)
1310 var dt = new Date();
1311
1312 //dt = Thu May 25 2006 (today's month/day in 2006)
1313 dt = Date.parseDate("2006", "Y");
1314
1315 //dt = Sun Jan 15 2006 (all date parts specified)
1316 dt = Date.parseDate("2006-1-15", "Y-m-d");
1317
1318 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1319 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1320 </code></pre>
1321  * @param {String} input The unparsed date as a string
1322  * @param {String} format The format the date is in
1323  * @return {Date} The parsed date
1324  * @static
1325  */
1326 Date.parseDate = function(input, format) {
1327     if (Date.parseFunctions[format] == null) {
1328         Date.createParser(format);
1329     }
1330     var func = Date.parseFunctions[format];
1331     return Date[func](input);
1332 };
1333 /**
1334  * @private
1335  */
1336
1337 Date.createParser = function(format) {
1338     var funcName = "parse" + Date.parseFunctions.count++;
1339     var regexNum = Date.parseRegexes.length;
1340     var currentGroup = 1;
1341     Date.parseFunctions[format] = funcName;
1342
1343     var code = "Date." + funcName + " = function(input){\n"
1344         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1345         + "var d = new Date();\n"
1346         + "y = d.getFullYear();\n"
1347         + "m = d.getMonth();\n"
1348         + "d = d.getDate();\n"
1349         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1350         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1351         + "if (results && results.length > 0) {";
1352     var regex = "";
1353
1354     var special = false;
1355     var ch = '';
1356     for (var i = 0; i < format.length; ++i) {
1357         ch = format.charAt(i);
1358         if (!special && ch == "\\") {
1359             special = true;
1360         }
1361         else if (special) {
1362             special = false;
1363             regex += String.escape(ch);
1364         }
1365         else {
1366             var obj = Date.formatCodeToRegex(ch, currentGroup);
1367             currentGroup += obj.g;
1368             regex += obj.s;
1369             if (obj.g && obj.c) {
1370                 code += obj.c;
1371             }
1372         }
1373     }
1374
1375     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1376         + "{v = new Date(y, m, d, h, i, s);}\n"
1377         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1378         + "{v = new Date(y, m, d, h, i);}\n"
1379         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1380         + "{v = new Date(y, m, d, h);}\n"
1381         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1382         + "{v = new Date(y, m, d);}\n"
1383         + "else if (y >= 0 && m >= 0)\n"
1384         + "{v = new Date(y, m);}\n"
1385         + "else if (y >= 0)\n"
1386         + "{v = new Date(y);}\n"
1387         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1388         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1389         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1390         + ";}";
1391
1392     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1393     /** eval:var:zzzzzzzzzzzzz */
1394     eval(code);
1395 };
1396
1397 // private
1398 Date.formatCodeToRegex = function(character, currentGroup) {
1399     switch (character) {
1400     case "D":
1401         return {g:0,
1402         c:null,
1403         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1404     case "j":
1405         return {g:1,
1406             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1407             s:"(\\d{1,2})"}; // day of month without leading zeroes
1408     case "d":
1409         return {g:1,
1410             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; // day of month with leading zeroes
1412     case "l":
1413         return {g:0,
1414             c:null,
1415             s:"(?:" + Date.dayNames.join("|") + ")"};
1416     case "S":
1417         return {g:0,
1418             c:null,
1419             s:"(?:st|nd|rd|th)"};
1420     case "w":
1421         return {g:0,
1422             c:null,
1423             s:"\\d"};
1424     case "z":
1425         return {g:0,
1426             c:null,
1427             s:"(?:\\d{1,3})"};
1428     case "W":
1429         return {g:0,
1430             c:null,
1431             s:"(?:\\d{2})"};
1432     case "F":
1433         return {g:1,
1434             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1435             s:"(" + Date.monthNames.join("|") + ")"};
1436     case "M":
1437         return {g:1,
1438             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1439             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1440     case "n":
1441         return {g:1,
1442             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1443             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1444     case "m":
1445         return {g:1,
1446             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1447             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1448     case "t":
1449         return {g:0,
1450             c:null,
1451             s:"\\d{1,2}"};
1452     case "L":
1453         return {g:0,
1454             c:null,
1455             s:"(?:1|0)"};
1456     case "Y":
1457         return {g:1,
1458             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{4})"};
1460     case "y":
1461         return {g:1,
1462             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1463                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1464             s:"(\\d{1,2})"};
1465     case "a":
1466         return {g:1,
1467             c:"if (results[" + currentGroup + "] == 'am') {\n"
1468                 + "if (h == 12) { h = 0; }\n"
1469                 + "} else { if (h < 12) { h += 12; }}",
1470             s:"(am|pm)"};
1471     case "A":
1472         return {g:1,
1473             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1474                 + "if (h == 12) { h = 0; }\n"
1475                 + "} else { if (h < 12) { h += 12; }}",
1476             s:"(AM|PM)"};
1477     case "g":
1478     case "G":
1479         return {g:1,
1480             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1481             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1482     case "h":
1483     case "H":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1487     case "i":
1488         return {g:1,
1489             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1490             s:"(\\d{2})"};
1491     case "s":
1492         return {g:1,
1493             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1494             s:"(\\d{2})"};
1495     case "O":
1496         return {g:1,
1497             c:[
1498                 "o = results[", currentGroup, "];\n",
1499                 "var sn = o.substring(0,1);\n", // get + / - sign
1500                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1501                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1502                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1503                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1504             ].join(""),
1505             s:"([+\-]\\d{2,4})"};
1506     
1507     
1508     case "P":
1509         return {g:1,
1510                 c:[
1511                    "o = results[", currentGroup, "];\n",
1512                    "var sn = o.substring(0,1);\n",
1513                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1514                    "var mn = o.substring(4,6) % 60;\n",
1515                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1516                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1517             ].join(""),
1518             s:"([+\-]\\d{4})"};
1519     case "T":
1520         return {g:0,
1521             c:null,
1522             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1523     case "Z":
1524         return {g:1,
1525             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1526                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1527             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1528     default:
1529         return {g:0,
1530             c:null,
1531             s:String.escape(character)};
1532     }
1533 };
1534
1535 /**
1536  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1537  * @return {String} The abbreviated timezone name (e.g. 'CST')
1538  */
1539 Date.prototype.getTimezone = function() {
1540     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1541 };
1542
1543 /**
1544  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1545  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1546  */
1547 Date.prototype.getGMTOffset = function() {
1548     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1549         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1550         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1551 };
1552
1553 /**
1554  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1555  * @return {String} 2-characters representing hours and 2-characters representing minutes
1556  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1557  */
1558 Date.prototype.getGMTColonOffset = function() {
1559         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1560                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1561                 + ":"
1562                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1563 }
1564
1565 /**
1566  * Get the numeric day number of the year, adjusted for leap year.
1567  * @return {Number} 0 through 364 (365 in leap years)
1568  */
1569 Date.prototype.getDayOfYear = function() {
1570     var num = 0;
1571     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1572     for (var i = 0; i < this.getMonth(); ++i) {
1573         num += Date.daysInMonth[i];
1574     }
1575     return num + this.getDate() - 1;
1576 };
1577
1578 /**
1579  * Get the string representation of the numeric week number of the year
1580  * (equivalent to the format specifier 'W').
1581  * @return {String} '00' through '52'
1582  */
1583 Date.prototype.getWeekOfYear = function() {
1584     // Skip to Thursday of this week
1585     var now = this.getDayOfYear() + (4 - this.getDay());
1586     // Find the first Thursday of the year
1587     var jan1 = new Date(this.getFullYear(), 0, 1);
1588     var then = (7 - jan1.getDay() + 4);
1589     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1590 };
1591
1592 /**
1593  * Whether or not the current date is in a leap year.
1594  * @return {Boolean} True if the current date is in a leap year, else false
1595  */
1596 Date.prototype.isLeapYear = function() {
1597     var year = this.getFullYear();
1598     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1599 };
1600
1601 /**
1602  * Get the first day of the current month, adjusted for leap year.  The returned value
1603  * is the numeric day index within the week (0-6) which can be used in conjunction with
1604  * the {@link #monthNames} array to retrieve the textual day name.
1605  * Example:
1606  *<pre><code>
1607 var dt = new Date('1/10/2007');
1608 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1609 </code></pre>
1610  * @return {Number} The day number (0-6)
1611  */
1612 Date.prototype.getFirstDayOfMonth = function() {
1613     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1614     return (day < 0) ? (day + 7) : day;
1615 };
1616
1617 /**
1618  * Get the last day of the current month, adjusted for leap year.  The returned value
1619  * is the numeric day index within the week (0-6) which can be used in conjunction with
1620  * the {@link #monthNames} array to retrieve the textual day name.
1621  * Example:
1622  *<pre><code>
1623 var dt = new Date('1/10/2007');
1624 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1625 </code></pre>
1626  * @return {Number} The day number (0-6)
1627  */
1628 Date.prototype.getLastDayOfMonth = function() {
1629     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1630     return (day < 0) ? (day + 7) : day;
1631 };
1632
1633
1634 /**
1635  * Get the first date of this date's month
1636  * @return {Date}
1637  */
1638 Date.prototype.getFirstDateOfMonth = function() {
1639     return new Date(this.getFullYear(), this.getMonth(), 1);
1640 };
1641
1642 /**
1643  * Get the last date of this date's month
1644  * @return {Date}
1645  */
1646 Date.prototype.getLastDateOfMonth = function() {
1647     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1648 };
1649 /**
1650  * Get the number of days in the current month, adjusted for leap year.
1651  * @return {Number} The number of days in the month
1652  */
1653 Date.prototype.getDaysInMonth = function() {
1654     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1655     return Date.daysInMonth[this.getMonth()];
1656 };
1657
1658 /**
1659  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1660  * @return {String} 'st, 'nd', 'rd' or 'th'
1661  */
1662 Date.prototype.getSuffix = function() {
1663     switch (this.getDate()) {
1664         case 1:
1665         case 21:
1666         case 31:
1667             return "st";
1668         case 2:
1669         case 22:
1670             return "nd";
1671         case 3:
1672         case 23:
1673             return "rd";
1674         default:
1675             return "th";
1676     }
1677 };
1678
1679 // private
1680 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1681
1682 /**
1683  * An array of textual month names.
1684  * Override these values for international dates, for example...
1685  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1686  * @type Array
1687  * @static
1688  */
1689 Date.monthNames =
1690    ["January",
1691     "February",
1692     "March",
1693     "April",
1694     "May",
1695     "June",
1696     "July",
1697     "August",
1698     "September",
1699     "October",
1700     "November",
1701     "December"];
1702
1703 /**
1704  * An array of textual day names.
1705  * Override these values for international dates, for example...
1706  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1707  * @type Array
1708  * @static
1709  */
1710 Date.dayNames =
1711    ["Sunday",
1712     "Monday",
1713     "Tuesday",
1714     "Wednesday",
1715     "Thursday",
1716     "Friday",
1717     "Saturday"];
1718
1719 // private
1720 Date.y2kYear = 50;
1721 // private
1722 Date.monthNumbers = {
1723     Jan:0,
1724     Feb:1,
1725     Mar:2,
1726     Apr:3,
1727     May:4,
1728     Jun:5,
1729     Jul:6,
1730     Aug:7,
1731     Sep:8,
1732     Oct:9,
1733     Nov:10,
1734     Dec:11};
1735
1736 /**
1737  * Creates and returns a new Date instance with the exact same date value as the called instance.
1738  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1739  * variable will also be changed.  When the intention is to create a new variable that will not
1740  * modify the original instance, you should create a clone.
1741  *
1742  * Example of correctly cloning a date:
1743  * <pre><code>
1744 //wrong way:
1745 var orig = new Date('10/1/2006');
1746 var copy = orig;
1747 copy.setDate(5);
1748 document.write(orig);  //returns 'Thu Oct 05 2006'!
1749
1750 //correct way:
1751 var orig = new Date('10/1/2006');
1752 var copy = orig.clone();
1753 copy.setDate(5);
1754 document.write(orig);  //returns 'Thu Oct 01 2006'
1755 </code></pre>
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.clone = function() {
1759         return new Date(this.getTime());
1760 };
1761
1762 /**
1763  * Clears any time information from this date
1764  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1765  @return {Date} this or the clone
1766  */
1767 Date.prototype.clearTime = function(clone){
1768     if(clone){
1769         return this.clone().clearTime();
1770     }
1771     this.setHours(0);
1772     this.setMinutes(0);
1773     this.setSeconds(0);
1774     this.setMilliseconds(0);
1775     return this;
1776 };
1777
1778 // private
1779 // safari setMonth is broken -- check that this is only donw once...
1780 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1781     Date.brokenSetMonth = Date.prototype.setMonth;
1782         Date.prototype.setMonth = function(num){
1783                 if(num <= -1){
1784                         var n = Math.ceil(-num);
1785                         var back_year = Math.ceil(n/12);
1786                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1787                         this.setFullYear(this.getFullYear() - back_year);
1788                         return Date.brokenSetMonth.call(this, month);
1789                 } else {
1790                         return Date.brokenSetMonth.apply(this, arguments);
1791                 }
1792         };
1793 }
1794
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.MILLI = "ms";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.SECOND = "s";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MINUTE = "mi";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.HOUR = "h";
1811 /** Date interval constant 
1812 * @static 
1813 * @type String */
1814 Date.DAY = "d";
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MONTH = "mo";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.YEAR = "y";
1823
1824 /**
1825  * Provides a convenient method of performing basic date arithmetic.  This method
1826  * does not modify the Date instance being called - it creates and returns
1827  * a new Date instance containing the resulting date value.
1828  *
1829  * Examples:
1830  * <pre><code>
1831 //Basic usage:
1832 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1833 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1834
1835 //Negative values will subtract correctly:
1836 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1837 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1838
1839 //You can even chain several calls together in one line!
1840 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1841 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1842  </code></pre>
1843  *
1844  * @param {String} interval   A valid date interval enum value
1845  * @param {Number} value      The amount to add to the current date
1846  * @return {Date} The new Date instance
1847  */
1848 Date.prototype.add = function(interval, value){
1849   var d = this.clone();
1850   if (!interval || value === 0) { return d; }
1851   switch(interval.toLowerCase()){
1852     case Date.MILLI:
1853       d.setMilliseconds(this.getMilliseconds() + value);
1854       break;
1855     case Date.SECOND:
1856       d.setSeconds(this.getSeconds() + value);
1857       break;
1858     case Date.MINUTE:
1859       d.setMinutes(this.getMinutes() + value);
1860       break;
1861     case Date.HOUR:
1862       d.setHours(this.getHours() + value);
1863       break;
1864     case Date.DAY:
1865       d.setDate(this.getDate() + value);
1866       break;
1867     case Date.MONTH:
1868       var day = this.getDate();
1869       if(day > 28){
1870           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1871       }
1872       d.setDate(day);
1873       d.setMonth(this.getMonth() + value);
1874       break;
1875     case Date.YEAR:
1876       d.setFullYear(this.getFullYear() + value);
1877       break;
1878   }
1879   return d;
1880 };
1881 /*
1882  * Based on:
1883  * Ext JS Library 1.1.1
1884  * Copyright(c) 2006-2007, Ext JS, LLC.
1885  *
1886  * Originally Released Under LGPL - original licence link has changed is not relivant.
1887  *
1888  * Fork - LGPL
1889  * <script type="text/javascript">
1890  */
1891
1892 /**
1893  * @class Roo.lib.Dom
1894  * @static
1895  * 
1896  * Dom utils (from YIU afaik)
1897  * 
1898  **/
1899 Roo.lib.Dom = {
1900     /**
1901      * Get the view width
1902      * @param {Boolean} full True will get the full document, otherwise it's the view width
1903      * @return {Number} The width
1904      */
1905      
1906     getViewWidth : function(full) {
1907         return full ? this.getDocumentWidth() : this.getViewportWidth();
1908     },
1909     /**
1910      * Get the view height
1911      * @param {Boolean} full True will get the full document, otherwise it's the view height
1912      * @return {Number} The height
1913      */
1914     getViewHeight : function(full) {
1915         return full ? this.getDocumentHeight() : this.getViewportHeight();
1916     },
1917
1918     getDocumentHeight: function() {
1919         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1920         return Math.max(scrollHeight, this.getViewportHeight());
1921     },
1922
1923     getDocumentWidth: function() {
1924         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1925         return Math.max(scrollWidth, this.getViewportWidth());
1926     },
1927
1928     getViewportHeight: function() {
1929         var height = self.innerHeight;
1930         var mode = document.compatMode;
1931
1932         if ((mode || Roo.isIE) && !Roo.isOpera) {
1933             height = (mode == "CSS1Compat") ?
1934                      document.documentElement.clientHeight :
1935                      document.body.clientHeight;
1936         }
1937
1938         return height;
1939     },
1940
1941     getViewportWidth: function() {
1942         var width = self.innerWidth;
1943         var mode = document.compatMode;
1944
1945         if (mode || Roo.isIE) {
1946             width = (mode == "CSS1Compat") ?
1947                     document.documentElement.clientWidth :
1948                     document.body.clientWidth;
1949         }
1950         return width;
1951     },
1952
1953     isAncestor : function(p, c) {
1954         p = Roo.getDom(p);
1955         c = Roo.getDom(c);
1956         if (!p || !c) {
1957             return false;
1958         }
1959
1960         if (p.contains && !Roo.isSafari) {
1961             return p.contains(c);
1962         } else if (p.compareDocumentPosition) {
1963             return !!(p.compareDocumentPosition(c) & 16);
1964         } else {
1965             var parent = c.parentNode;
1966             while (parent) {
1967                 if (parent == p) {
1968                     return true;
1969                 }
1970                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1971                     return false;
1972                 }
1973                 parent = parent.parentNode;
1974             }
1975             return false;
1976         }
1977     },
1978
1979     getRegion : function(el) {
1980         return Roo.lib.Region.getRegion(el);
1981     },
1982
1983     getY : function(el) {
1984         return this.getXY(el)[1];
1985     },
1986
1987     getX : function(el) {
1988         return this.getXY(el)[0];
1989     },
1990
1991     getXY : function(el) {
1992         var p, pe, b, scroll, bd = document.body;
1993         el = Roo.getDom(el);
1994         var fly = Roo.lib.AnimBase.fly;
1995         if (el.getBoundingClientRect) {
1996             b = el.getBoundingClientRect();
1997             scroll = fly(document).getScroll();
1998             return [b.left + scroll.left, b.top + scroll.top];
1999         }
2000         var x = 0, y = 0;
2001
2002         p = el;
2003
2004         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2005
2006         while (p) {
2007
2008             x += p.offsetLeft;
2009             y += p.offsetTop;
2010
2011             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2012                 hasAbsolute = true;
2013             }
2014
2015             if (Roo.isGecko) {
2016                 pe = fly(p);
2017
2018                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2019                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2020
2021
2022                 x += bl;
2023                 y += bt;
2024
2025
2026                 if (p != el && pe.getStyle('overflow') != 'visible') {
2027                     x += bl;
2028                     y += bt;
2029                 }
2030             }
2031             p = p.offsetParent;
2032         }
2033
2034         if (Roo.isSafari && hasAbsolute) {
2035             x -= bd.offsetLeft;
2036             y -= bd.offsetTop;
2037         }
2038
2039         if (Roo.isGecko && !hasAbsolute) {
2040             var dbd = fly(bd);
2041             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2042             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2043         }
2044
2045         p = el.parentNode;
2046         while (p && p != bd) {
2047             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2048                 x -= p.scrollLeft;
2049                 y -= p.scrollTop;
2050             }
2051             p = p.parentNode;
2052         }
2053         return [x, y];
2054     },
2055  
2056   
2057
2058
2059     setXY : function(el, xy) {
2060         el = Roo.fly(el, '_setXY');
2061         el.position();
2062         var pts = el.translatePoints(xy);
2063         if (xy[0] !== false) {
2064             el.dom.style.left = pts.left + "px";
2065         }
2066         if (xy[1] !== false) {
2067             el.dom.style.top = pts.top + "px";
2068         }
2069     },
2070
2071     setX : function(el, x) {
2072         this.setXY(el, [x, false]);
2073     },
2074
2075     setY : function(el, y) {
2076         this.setXY(el, [false, y]);
2077     }
2078 };
2079 /*
2080  * Portions of this file are based on pieces of Yahoo User Interface Library
2081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2082  * YUI licensed under the BSD License:
2083  * http://developer.yahoo.net/yui/license.txt
2084  * <script type="text/javascript">
2085  *
2086  */
2087
2088 Roo.lib.Event = function() {
2089     var loadComplete = false;
2090     var listeners = [];
2091     var unloadListeners = [];
2092     var retryCount = 0;
2093     var onAvailStack = [];
2094     var counter = 0;
2095     var lastError = null;
2096
2097     return {
2098         POLL_RETRYS: 200,
2099         POLL_INTERVAL: 20,
2100         EL: 0,
2101         TYPE: 1,
2102         FN: 2,
2103         WFN: 3,
2104         OBJ: 3,
2105         ADJ_SCOPE: 4,
2106         _interval: null,
2107
2108         startInterval: function() {
2109             if (!this._interval) {
2110                 var self = this;
2111                 var callback = function() {
2112                     self._tryPreloadAttach();
2113                 };
2114                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2115
2116             }
2117         },
2118
2119         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2120             onAvailStack.push({ id:         p_id,
2121                 fn:         p_fn,
2122                 obj:        p_obj,
2123                 override:   p_override,
2124                 checkReady: false    });
2125
2126             retryCount = this.POLL_RETRYS;
2127             this.startInterval();
2128         },
2129
2130
2131         addListener: function(el, eventName, fn) {
2132             el = Roo.getDom(el);
2133             if (!el || !fn) {
2134                 return false;
2135             }
2136
2137             if ("unload" == eventName) {
2138                 unloadListeners[unloadListeners.length] =
2139                 [el, eventName, fn];
2140                 return true;
2141             }
2142
2143             var wrappedFn = function(e) {
2144                 return fn(Roo.lib.Event.getEvent(e));
2145             };
2146
2147             var li = [el, eventName, fn, wrappedFn];
2148
2149             var index = listeners.length;
2150             listeners[index] = li;
2151
2152             this.doAdd(el, eventName, wrappedFn, false);
2153             return true;
2154
2155         },
2156
2157
2158         removeListener: function(el, eventName, fn) {
2159             var i, len;
2160
2161             el = Roo.getDom(el);
2162
2163             if(!fn) {
2164                 return this.purgeElement(el, false, eventName);
2165             }
2166
2167
2168             if ("unload" == eventName) {
2169
2170                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2171                     var li = unloadListeners[i];
2172                     if (li &&
2173                         li[0] == el &&
2174                         li[1] == eventName &&
2175                         li[2] == fn) {
2176                         unloadListeners.splice(i, 1);
2177                         return true;
2178                     }
2179                 }
2180
2181                 return false;
2182             }
2183
2184             var cacheItem = null;
2185
2186
2187             var index = arguments[3];
2188
2189             if ("undefined" == typeof index) {
2190                 index = this._getCacheIndex(el, eventName, fn);
2191             }
2192
2193             if (index >= 0) {
2194                 cacheItem = listeners[index];
2195             }
2196
2197             if (!el || !cacheItem) {
2198                 return false;
2199             }
2200
2201             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2202
2203             delete listeners[index][this.WFN];
2204             delete listeners[index][this.FN];
2205             listeners.splice(index, 1);
2206
2207             return true;
2208
2209         },
2210
2211
2212         getTarget: function(ev, resolveTextNode) {
2213             ev = ev.browserEvent || ev;
2214             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2215             var t = ev.target || ev.srcElement;
2216             return this.resolveTextNode(t);
2217         },
2218
2219
2220         resolveTextNode: function(node) {
2221             if (Roo.isSafari && node && 3 == node.nodeType) {
2222                 return node.parentNode;
2223             } else {
2224                 return node;
2225             }
2226         },
2227
2228
2229         getPageX: function(ev) {
2230             ev = ev.browserEvent || ev;
2231             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2232             var x = ev.pageX;
2233             if (!x && 0 !== x) {
2234                 x = ev.clientX || 0;
2235
2236                 if (Roo.isIE) {
2237                     x += this.getScroll()[1];
2238                 }
2239             }
2240
2241             return x;
2242         },
2243
2244
2245         getPageY: function(ev) {
2246             ev = ev.browserEvent || ev;
2247             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2248             var y = ev.pageY;
2249             if (!y && 0 !== y) {
2250                 y = ev.clientY || 0;
2251
2252                 if (Roo.isIE) {
2253                     y += this.getScroll()[0];
2254                 }
2255             }
2256
2257
2258             return y;
2259         },
2260
2261
2262         getXY: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             return [this.getPageX(ev), this.getPageY(ev)];
2266         },
2267
2268
2269         getRelatedTarget: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2272             var t = ev.relatedTarget;
2273             if (!t) {
2274                 if (ev.type == "mouseout") {
2275                     t = ev.toElement;
2276                 } else if (ev.type == "mouseover") {
2277                     t = ev.fromElement;
2278                 }
2279             }
2280
2281             return this.resolveTextNode(t);
2282         },
2283
2284
2285         getTime: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             if (!ev.time) {
2289                 var t = new Date().getTime();
2290                 try {
2291                     ev.time = t;
2292                 } catch(ex) {
2293                     this.lastError = ex;
2294                     return t;
2295                 }
2296             }
2297
2298             return ev.time;
2299         },
2300
2301
2302         stopEvent: function(ev) {
2303             this.stopPropagation(ev);
2304             this.preventDefault(ev);
2305         },
2306
2307
2308         stopPropagation: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             if (ev.stopPropagation) {
2311                 ev.stopPropagation();
2312             } else {
2313                 ev.cancelBubble = true;
2314             }
2315         },
2316
2317
2318         preventDefault: function(ev) {
2319             ev = ev.browserEvent || ev;
2320             if(ev.preventDefault) {
2321                 ev.preventDefault();
2322             } else {
2323                 ev.returnValue = false;
2324             }
2325         },
2326
2327
2328         getEvent: function(e) {
2329             var ev = e || window.event;
2330             if (!ev) {
2331                 var c = this.getEvent.caller;
2332                 while (c) {
2333                     ev = c.arguments[0];
2334                     if (ev && Event == ev.constructor) {
2335                         break;
2336                     }
2337                     c = c.caller;
2338                 }
2339             }
2340             return ev;
2341         },
2342
2343
2344         getCharCode: function(ev) {
2345             ev = ev.browserEvent || ev;
2346             return ev.charCode || ev.keyCode || 0;
2347         },
2348
2349
2350         _getCacheIndex: function(el, eventName, fn) {
2351             for (var i = 0,len = listeners.length; i < len; ++i) {
2352                 var li = listeners[i];
2353                 if (li &&
2354                     li[this.FN] == fn &&
2355                     li[this.EL] == el &&
2356                     li[this.TYPE] == eventName) {
2357                     return i;
2358                 }
2359             }
2360
2361             return -1;
2362         },
2363
2364
2365         elCache: {},
2366
2367
2368         getEl: function(id) {
2369             return document.getElementById(id);
2370         },
2371
2372
2373         clearCache: function() {
2374         },
2375
2376
2377         _load: function(e) {
2378             loadComplete = true;
2379             var EU = Roo.lib.Event;
2380
2381
2382             if (Roo.isIE) {
2383                 EU.doRemove(window, "load", EU._load);
2384             }
2385         },
2386
2387
2388         _tryPreloadAttach: function() {
2389
2390             if (this.locked) {
2391                 return false;
2392             }
2393
2394             this.locked = true;
2395
2396
2397             var tryAgain = !loadComplete;
2398             if (!tryAgain) {
2399                 tryAgain = (retryCount > 0);
2400             }
2401
2402
2403             var notAvail = [];
2404             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2405                 var item = onAvailStack[i];
2406                 if (item) {
2407                     var el = this.getEl(item.id);
2408
2409                     if (el) {
2410                         if (!item.checkReady ||
2411                             loadComplete ||
2412                             el.nextSibling ||
2413                             (document && document.body)) {
2414
2415                             var scope = el;
2416                             if (item.override) {
2417                                 if (item.override === true) {
2418                                     scope = item.obj;
2419                                 } else {
2420                                     scope = item.override;
2421                                 }
2422                             }
2423                             item.fn.call(scope, item.obj);
2424                             onAvailStack[i] = null;
2425                         }
2426                     } else {
2427                         notAvail.push(item);
2428                     }
2429                 }
2430             }
2431
2432             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2433
2434             if (tryAgain) {
2435
2436                 this.startInterval();
2437             } else {
2438                 clearInterval(this._interval);
2439                 this._interval = null;
2440             }
2441
2442             this.locked = false;
2443
2444             return true;
2445
2446         },
2447
2448
2449         purgeElement: function(el, recurse, eventName) {
2450             var elListeners = this.getListeners(el, eventName);
2451             if (elListeners) {
2452                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2453                     var l = elListeners[i];
2454                     this.removeListener(el, l.type, l.fn);
2455                 }
2456             }
2457
2458             if (recurse && el && el.childNodes) {
2459                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2460                     this.purgeElement(el.childNodes[i], recurse, eventName);
2461                 }
2462             }
2463         },
2464
2465
2466         getListeners: function(el, eventName) {
2467             var results = [], searchLists;
2468             if (!eventName) {
2469                 searchLists = [listeners, unloadListeners];
2470             } else if (eventName == "unload") {
2471                 searchLists = [unloadListeners];
2472             } else {
2473                 searchLists = [listeners];
2474             }
2475
2476             for (var j = 0; j < searchLists.length; ++j) {
2477                 var searchList = searchLists[j];
2478                 if (searchList && searchList.length > 0) {
2479                     for (var i = 0,len = searchList.length; i < len; ++i) {
2480                         var l = searchList[i];
2481                         if (l && l[this.EL] === el &&
2482                             (!eventName || eventName === l[this.TYPE])) {
2483                             results.push({
2484                                 type:   l[this.TYPE],
2485                                 fn:     l[this.FN],
2486                                 obj:    l[this.OBJ],
2487                                 adjust: l[this.ADJ_SCOPE],
2488                                 index:  i
2489                             });
2490                         }
2491                     }
2492                 }
2493             }
2494
2495             return (results.length) ? results : null;
2496         },
2497
2498
2499         _unload: function(e) {
2500
2501             var EU = Roo.lib.Event, i, j, l, len, index;
2502
2503             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2504                 l = unloadListeners[i];
2505                 if (l) {
2506                     var scope = window;
2507                     if (l[EU.ADJ_SCOPE]) {
2508                         if (l[EU.ADJ_SCOPE] === true) {
2509                             scope = l[EU.OBJ];
2510                         } else {
2511                             scope = l[EU.ADJ_SCOPE];
2512                         }
2513                     }
2514                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2515                     unloadListeners[i] = null;
2516                     l = null;
2517                     scope = null;
2518                 }
2519             }
2520
2521             unloadListeners = null;
2522
2523             if (listeners && listeners.length > 0) {
2524                 j = listeners.length;
2525                 while (j) {
2526                     index = j - 1;
2527                     l = listeners[index];
2528                     if (l) {
2529                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2530                                 l[EU.FN], index);
2531                     }
2532                     j = j - 1;
2533                 }
2534                 l = null;
2535
2536                 EU.clearCache();
2537             }
2538
2539             EU.doRemove(window, "unload", EU._unload);
2540
2541         },
2542
2543
2544         getScroll: function() {
2545             var dd = document.documentElement, db = document.body;
2546             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2547                 return [dd.scrollTop, dd.scrollLeft];
2548             } else if (db) {
2549                 return [db.scrollTop, db.scrollLeft];
2550             } else {
2551                 return [0, 0];
2552             }
2553         },
2554
2555
2556         doAdd: function () {
2557             if (window.addEventListener) {
2558                 return function(el, eventName, fn, capture) {
2559                     el.addEventListener(eventName, fn, (capture));
2560                 };
2561             } else if (window.attachEvent) {
2562                 return function(el, eventName, fn, capture) {
2563                     el.attachEvent("on" + eventName, fn);
2564                 };
2565             } else {
2566                 return function() {
2567                 };
2568             }
2569         }(),
2570
2571
2572         doRemove: function() {
2573             if (window.removeEventListener) {
2574                 return function (el, eventName, fn, capture) {
2575                     el.removeEventListener(eventName, fn, (capture));
2576                 };
2577             } else if (window.detachEvent) {
2578                 return function (el, eventName, fn) {
2579                     el.detachEvent("on" + eventName, fn);
2580                 };
2581             } else {
2582                 return function() {
2583                 };
2584             }
2585         }()
2586     };
2587     
2588 }();
2589 (function() {     
2590    
2591     var E = Roo.lib.Event;
2592     E.on = E.addListener;
2593     E.un = E.removeListener;
2594
2595     if (document && document.body) {
2596         E._load();
2597     } else {
2598         E.doAdd(window, "load", E._load);
2599     }
2600     E.doAdd(window, "unload", E._unload);
2601     E._tryPreloadAttach();
2602 })();
2603
2604 /*
2605  * Portions of this file are based on pieces of Yahoo User Interface Library
2606  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2607  * YUI licensed under the BSD License:
2608  * http://developer.yahoo.net/yui/license.txt
2609  * <script type="text/javascript">
2610  *
2611  */
2612
2613 (function() {
2614     /**
2615      * @class Roo.lib.Ajax
2616      *
2617      */
2618     Roo.lib.Ajax = {
2619         /**
2620          * @static 
2621          */
2622         request : function(method, uri, cb, data, options) {
2623             if(options){
2624                 var hs = options.headers;
2625                 if(hs){
2626                     for(var h in hs){
2627                         if(hs.hasOwnProperty(h)){
2628                             this.initHeader(h, hs[h], false);
2629                         }
2630                     }
2631                 }
2632                 if(options.xmlData){
2633                     this.initHeader('Content-Type', 'text/xml', false);
2634                     method = 'POST';
2635                     data = options.xmlData;
2636                 }
2637             }
2638
2639             return this.asyncRequest(method, uri, cb, data);
2640         },
2641
2642         serializeForm : function(form) {
2643             if(typeof form == 'string') {
2644                 form = (document.getElementById(form) || document.forms[form]);
2645             }
2646
2647             var el, name, val, disabled, data = '', hasSubmit = false;
2648             for (var i = 0; i < form.elements.length; i++) {
2649                 el = form.elements[i];
2650                 disabled = form.elements[i].disabled;
2651                 name = form.elements[i].name;
2652                 val = form.elements[i].value;
2653
2654                 if (!disabled && name){
2655                     switch (el.type)
2656                             {
2657                         case 'select-one':
2658                         case 'select-multiple':
2659                             for (var j = 0; j < el.options.length; j++) {
2660                                 if (el.options[j].selected) {
2661                                     if (Roo.isIE) {
2662                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2663                                     }
2664                                     else {
2665                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2666                                     }
2667                                 }
2668                             }
2669                             break;
2670                         case 'radio':
2671                         case 'checkbox':
2672                             if (el.checked) {
2673                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2674                             }
2675                             break;
2676                         case 'file':
2677
2678                         case undefined:
2679
2680                         case 'reset':
2681
2682                         case 'button':
2683
2684                             break;
2685                         case 'submit':
2686                             if(hasSubmit == false) {
2687                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2688                                 hasSubmit = true;
2689                             }
2690                             break;
2691                         default:
2692                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2693                             break;
2694                     }
2695                 }
2696             }
2697             data = data.substr(0, data.length - 1);
2698             return data;
2699         },
2700
2701         headers:{},
2702
2703         hasHeaders:false,
2704
2705         useDefaultHeader:true,
2706
2707         defaultPostHeader:'application/x-www-form-urlencoded',
2708
2709         useDefaultXhrHeader:true,
2710
2711         defaultXhrHeader:'XMLHttpRequest',
2712
2713         hasDefaultHeaders:true,
2714
2715         defaultHeaders:{},
2716
2717         poll:{},
2718
2719         timeout:{},
2720
2721         pollInterval:50,
2722
2723         transactionId:0,
2724
2725         setProgId:function(id)
2726         {
2727             this.activeX.unshift(id);
2728         },
2729
2730         setDefaultPostHeader:function(b)
2731         {
2732             this.useDefaultHeader = b;
2733         },
2734
2735         setDefaultXhrHeader:function(b)
2736         {
2737             this.useDefaultXhrHeader = b;
2738         },
2739
2740         setPollingInterval:function(i)
2741         {
2742             if (typeof i == 'number' && isFinite(i)) {
2743                 this.pollInterval = i;
2744             }
2745         },
2746
2747         createXhrObject:function(transactionId)
2748         {
2749             var obj,http;
2750             try
2751             {
2752
2753                 http = new XMLHttpRequest();
2754
2755                 obj = { conn:http, tId:transactionId };
2756             }
2757             catch(e)
2758             {
2759                 for (var i = 0; i < this.activeX.length; ++i) {
2760                     try
2761                     {
2762
2763                         http = new ActiveXObject(this.activeX[i]);
2764
2765                         obj = { conn:http, tId:transactionId };
2766                         break;
2767                     }
2768                     catch(e) {
2769                     }
2770                 }
2771             }
2772             finally
2773             {
2774                 return obj;
2775             }
2776         },
2777
2778         getConnectionObject:function()
2779         {
2780             var o;
2781             var tId = this.transactionId;
2782
2783             try
2784             {
2785                 o = this.createXhrObject(tId);
2786                 if (o) {
2787                     this.transactionId++;
2788                 }
2789             }
2790             catch(e) {
2791             }
2792             finally
2793             {
2794                 return o;
2795             }
2796         },
2797
2798         asyncRequest:function(method, uri, callback, postData)
2799         {
2800             var o = this.getConnectionObject();
2801
2802             if (!o) {
2803                 return null;
2804             }
2805             else {
2806                 o.conn.open(method, uri, true);
2807
2808                 if (this.useDefaultXhrHeader) {
2809                     if (!this.defaultHeaders['X-Requested-With']) {
2810                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2811                     }
2812                 }
2813
2814                 if(postData && this.useDefaultHeader){
2815                     this.initHeader('Content-Type', this.defaultPostHeader);
2816                 }
2817
2818                  if (this.hasDefaultHeaders || this.hasHeaders) {
2819                     this.setHeader(o);
2820                 }
2821
2822                 this.handleReadyState(o, callback);
2823                 o.conn.send(postData || null);
2824
2825                 return o;
2826             }
2827         },
2828
2829         handleReadyState:function(o, callback)
2830         {
2831             var oConn = this;
2832
2833             if (callback && callback.timeout) {
2834                 
2835                 this.timeout[o.tId] = window.setTimeout(function() {
2836                     oConn.abort(o, callback, true);
2837                 }, callback.timeout);
2838             }
2839
2840             this.poll[o.tId] = window.setInterval(
2841                     function() {
2842                         if (o.conn && o.conn.readyState == 4) {
2843                             window.clearInterval(oConn.poll[o.tId]);
2844                             delete oConn.poll[o.tId];
2845
2846                             if(callback && callback.timeout) {
2847                                 window.clearTimeout(oConn.timeout[o.tId]);
2848                                 delete oConn.timeout[o.tId];
2849                             }
2850
2851                             oConn.handleTransactionResponse(o, callback);
2852                         }
2853                     }
2854                     , this.pollInterval);
2855         },
2856
2857         handleTransactionResponse:function(o, callback, isAbort)
2858         {
2859
2860             if (!callback) {
2861                 this.releaseObject(o);
2862                 return;
2863             }
2864
2865             var httpStatus, responseObject;
2866
2867             try
2868             {
2869                 if (o.conn.status !== undefined && o.conn.status != 0) {
2870                     httpStatus = o.conn.status;
2871                 }
2872                 else {
2873                     httpStatus = 13030;
2874                 }
2875             }
2876             catch(e) {
2877
2878
2879                 httpStatus = 13030;
2880             }
2881
2882             if (httpStatus >= 200 && httpStatus < 300) {
2883                 responseObject = this.createResponseObject(o, callback.argument);
2884                 if (callback.success) {
2885                     if (!callback.scope) {
2886                         callback.success(responseObject);
2887                     }
2888                     else {
2889
2890
2891                         callback.success.apply(callback.scope, [responseObject]);
2892                     }
2893                 }
2894             }
2895             else {
2896                 switch (httpStatus) {
2897
2898                     case 12002:
2899                     case 12029:
2900                     case 12030:
2901                     case 12031:
2902                     case 12152:
2903                     case 13030:
2904                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2905                         if (callback.failure) {
2906                             if (!callback.scope) {
2907                                 callback.failure(responseObject);
2908                             }
2909                             else {
2910                                 callback.failure.apply(callback.scope, [responseObject]);
2911                             }
2912                         }
2913                         break;
2914                     default:
2915                         responseObject = this.createResponseObject(o, callback.argument);
2916                         if (callback.failure) {
2917                             if (!callback.scope) {
2918                                 callback.failure(responseObject);
2919                             }
2920                             else {
2921                                 callback.failure.apply(callback.scope, [responseObject]);
2922                             }
2923                         }
2924                 }
2925             }
2926
2927             this.releaseObject(o);
2928             responseObject = null;
2929         },
2930
2931         createResponseObject:function(o, callbackArg)
2932         {
2933             var obj = {};
2934             var headerObj = {};
2935
2936             try
2937             {
2938                 var headerStr = o.conn.getAllResponseHeaders();
2939                 var header = headerStr.split('\n');
2940                 for (var i = 0; i < header.length; i++) {
2941                     var delimitPos = header[i].indexOf(':');
2942                     if (delimitPos != -1) {
2943                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2944                     }
2945                 }
2946             }
2947             catch(e) {
2948             }
2949
2950             obj.tId = o.tId;
2951             obj.status = o.conn.status;
2952             obj.statusText = o.conn.statusText;
2953             obj.getResponseHeader = headerObj;
2954             obj.getAllResponseHeaders = headerStr;
2955             obj.responseText = o.conn.responseText;
2956             obj.responseXML = o.conn.responseXML;
2957
2958             if (typeof callbackArg !== undefined) {
2959                 obj.argument = callbackArg;
2960             }
2961
2962             return obj;
2963         },
2964
2965         createExceptionObject:function(tId, callbackArg, isAbort)
2966         {
2967             var COMM_CODE = 0;
2968             var COMM_ERROR = 'communication failure';
2969             var ABORT_CODE = -1;
2970             var ABORT_ERROR = 'transaction aborted';
2971
2972             var obj = {};
2973
2974             obj.tId = tId;
2975             if (isAbort) {
2976                 obj.status = ABORT_CODE;
2977                 obj.statusText = ABORT_ERROR;
2978             }
2979             else {
2980                 obj.status = COMM_CODE;
2981                 obj.statusText = COMM_ERROR;
2982             }
2983
2984             if (callbackArg) {
2985                 obj.argument = callbackArg;
2986             }
2987
2988             return obj;
2989         },
2990
2991         initHeader:function(label, value, isDefault)
2992         {
2993             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2994
2995             if (headerObj[label] === undefined) {
2996                 headerObj[label] = value;
2997             }
2998             else {
2999
3000
3001                 headerObj[label] = value + "," + headerObj[label];
3002             }
3003
3004             if (isDefault) {
3005                 this.hasDefaultHeaders = true;
3006             }
3007             else {
3008                 this.hasHeaders = true;
3009             }
3010         },
3011
3012
3013         setHeader:function(o)
3014         {
3015             if (this.hasDefaultHeaders) {
3016                 for (var prop in this.defaultHeaders) {
3017                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3018                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3019                     }
3020                 }
3021             }
3022
3023             if (this.hasHeaders) {
3024                 for (var prop in this.headers) {
3025                     if (this.headers.hasOwnProperty(prop)) {
3026                         o.conn.setRequestHeader(prop, this.headers[prop]);
3027                     }
3028                 }
3029                 this.headers = {};
3030                 this.hasHeaders = false;
3031             }
3032         },
3033
3034         resetDefaultHeaders:function() {
3035             delete this.defaultHeaders;
3036             this.defaultHeaders = {};
3037             this.hasDefaultHeaders = false;
3038         },
3039
3040         abort:function(o, callback, isTimeout)
3041         {
3042             if(this.isCallInProgress(o)) {
3043                 o.conn.abort();
3044                 window.clearInterval(this.poll[o.tId]);
3045                 delete this.poll[o.tId];
3046                 if (isTimeout) {
3047                     delete this.timeout[o.tId];
3048                 }
3049
3050                 this.handleTransactionResponse(o, callback, true);
3051
3052                 return true;
3053             }
3054             else {
3055                 return false;
3056             }
3057         },
3058
3059
3060         isCallInProgress:function(o)
3061         {
3062             if (o && o.conn) {
3063                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3064             }
3065             else {
3066
3067                 return false;
3068             }
3069         },
3070
3071
3072         releaseObject:function(o)
3073         {
3074
3075             o.conn = null;
3076
3077             o = null;
3078         },
3079
3080         activeX:[
3081         'MSXML2.XMLHTTP.3.0',
3082         'MSXML2.XMLHTTP',
3083         'Microsoft.XMLHTTP'
3084         ]
3085
3086
3087     };
3088 })();/*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096
3097 Roo.lib.Region = function(t, r, b, l) {
3098     this.top = t;
3099     this[1] = t;
3100     this.right = r;
3101     this.bottom = b;
3102     this.left = l;
3103     this[0] = l;
3104 };
3105
3106
3107 Roo.lib.Region.prototype = {
3108     contains : function(region) {
3109         return ( region.left >= this.left &&
3110                  region.right <= this.right &&
3111                  region.top >= this.top &&
3112                  region.bottom <= this.bottom    );
3113
3114     },
3115
3116     getArea : function() {
3117         return ( (this.bottom - this.top) * (this.right - this.left) );
3118     },
3119
3120     intersect : function(region) {
3121         var t = Math.max(this.top, region.top);
3122         var r = Math.min(this.right, region.right);
3123         var b = Math.min(this.bottom, region.bottom);
3124         var l = Math.max(this.left, region.left);
3125
3126         if (b >= t && r >= l) {
3127             return new Roo.lib.Region(t, r, b, l);
3128         } else {
3129             return null;
3130         }
3131     },
3132     union : function(region) {
3133         var t = Math.min(this.top, region.top);
3134         var r = Math.max(this.right, region.right);
3135         var b = Math.max(this.bottom, region.bottom);
3136         var l = Math.min(this.left, region.left);
3137
3138         return new Roo.lib.Region(t, r, b, l);
3139     },
3140
3141     adjust : function(t, l, b, r) {
3142         this.top += t;
3143         this.left += l;
3144         this.right += r;
3145         this.bottom += b;
3146         return this;
3147     }
3148 };
3149
3150 Roo.lib.Region.getRegion = function(el) {
3151     var p = Roo.lib.Dom.getXY(el);
3152
3153     var t = p[1];
3154     var r = p[0] + el.offsetWidth;
3155     var b = p[1] + el.offsetHeight;
3156     var l = p[0];
3157
3158     return new Roo.lib.Region(t, r, b, l);
3159 };
3160 /*
3161  * Portions of this file are based on pieces of Yahoo User Interface Library
3162  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3163  * YUI licensed under the BSD License:
3164  * http://developer.yahoo.net/yui/license.txt
3165  * <script type="text/javascript">
3166  *
3167  */
3168 //@@dep Roo.lib.Region
3169
3170
3171 Roo.lib.Point = function(x, y) {
3172     if (x instanceof Array) {
3173         y = x[1];
3174         x = x[0];
3175     }
3176     this.x = this.right = this.left = this[0] = x;
3177     this.y = this.top = this.bottom = this[1] = y;
3178 };
3179
3180 Roo.lib.Point.prototype = new Roo.lib.Region();
3181 /*
3182  * Portions of this file are based on pieces of Yahoo User Interface Library
3183  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3184  * YUI licensed under the BSD License:
3185  * http://developer.yahoo.net/yui/license.txt
3186  * <script type="text/javascript">
3187  *
3188  */
3189  
3190 (function() {   
3191
3192     Roo.lib.Anim = {
3193         scroll : function(el, args, duration, easing, cb, scope) {
3194             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3195         },
3196
3197         motion : function(el, args, duration, easing, cb, scope) {
3198             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3199         },
3200
3201         color : function(el, args, duration, easing, cb, scope) {
3202             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3203         },
3204
3205         run : function(el, args, duration, easing, cb, scope, type) {
3206             type = type || Roo.lib.AnimBase;
3207             if (typeof easing == "string") {
3208                 easing = Roo.lib.Easing[easing];
3209             }
3210             var anim = new type(el, args, duration, easing);
3211             anim.animateX(function() {
3212                 Roo.callback(cb, scope);
3213             });
3214             return anim;
3215         }
3216     };
3217 })();/*
3218  * Portions of this file are based on pieces of Yahoo User Interface Library
3219  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3220  * YUI licensed under the BSD License:
3221  * http://developer.yahoo.net/yui/license.txt
3222  * <script type="text/javascript">
3223  *
3224  */
3225
3226 (function() {    
3227     var libFlyweight;
3228     
3229     function fly(el) {
3230         if (!libFlyweight) {
3231             libFlyweight = new Roo.Element.Flyweight();
3232         }
3233         libFlyweight.dom = el;
3234         return libFlyweight;
3235     }
3236
3237     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3238     
3239    
3240     
3241     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3242         if (el) {
3243             this.init(el, attributes, duration, method);
3244         }
3245     };
3246
3247     Roo.lib.AnimBase.fly = fly;
3248     
3249     
3250     
3251     Roo.lib.AnimBase.prototype = {
3252
3253         toString: function() {
3254             var el = this.getEl();
3255             var id = el.id || el.tagName;
3256             return ("Anim " + id);
3257         },
3258
3259         patterns: {
3260             noNegatives:        /width|height|opacity|padding/i,
3261             offsetAttribute:  /^((width|height)|(top|left))$/,
3262             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3263             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3264         },
3265
3266
3267         doMethod: function(attr, start, end) {
3268             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3269         },
3270
3271
3272         setAttribute: function(attr, val, unit) {
3273             if (this.patterns.noNegatives.test(attr)) {
3274                 val = (val > 0) ? val : 0;
3275             }
3276
3277             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3278         },
3279
3280
3281         getAttribute: function(attr) {
3282             var el = this.getEl();
3283             var val = fly(el).getStyle(attr);
3284
3285             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3286                 return parseFloat(val);
3287             }
3288
3289             var a = this.patterns.offsetAttribute.exec(attr) || [];
3290             var pos = !!( a[3] );
3291             var box = !!( a[2] );
3292
3293
3294             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3295                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3296             } else {
3297                 val = 0;
3298             }
3299
3300             return val;
3301         },
3302
3303
3304         getDefaultUnit: function(attr) {
3305             if (this.patterns.defaultUnit.test(attr)) {
3306                 return 'px';
3307             }
3308
3309             return '';
3310         },
3311
3312         animateX : function(callback, scope) {
3313             var f = function() {
3314                 this.onComplete.removeListener(f);
3315                 if (typeof callback == "function") {
3316                     callback.call(scope || this, this);
3317                 }
3318             };
3319             this.onComplete.addListener(f, this);
3320             this.animate();
3321         },
3322
3323
3324         setRuntimeAttribute: function(attr) {
3325             var start;
3326             var end;
3327             var attributes = this.attributes;
3328
3329             this.runtimeAttributes[attr] = {};
3330
3331             var isset = function(prop) {
3332                 return (typeof prop !== 'undefined');
3333             };
3334
3335             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3336                 return false;
3337             }
3338
3339             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3340
3341
3342             if (isset(attributes[attr]['to'])) {
3343                 end = attributes[attr]['to'];
3344             } else if (isset(attributes[attr]['by'])) {
3345                 if (start.constructor == Array) {
3346                     end = [];
3347                     for (var i = 0, len = start.length; i < len; ++i) {
3348                         end[i] = start[i] + attributes[attr]['by'][i];
3349                     }
3350                 } else {
3351                     end = start + attributes[attr]['by'];
3352                 }
3353             }
3354
3355             this.runtimeAttributes[attr].start = start;
3356             this.runtimeAttributes[attr].end = end;
3357
3358
3359             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3360         },
3361
3362
3363         init: function(el, attributes, duration, method) {
3364
3365             var isAnimated = false;
3366
3367
3368             var startTime = null;
3369
3370
3371             var actualFrames = 0;
3372
3373
3374             el = Roo.getDom(el);
3375
3376
3377             this.attributes = attributes || {};
3378
3379
3380             this.duration = duration || 1;
3381
3382
3383             this.method = method || Roo.lib.Easing.easeNone;
3384
3385
3386             this.useSeconds = true;
3387
3388
3389             this.currentFrame = 0;
3390
3391
3392             this.totalFrames = Roo.lib.AnimMgr.fps;
3393
3394
3395             this.getEl = function() {
3396                 return el;
3397             };
3398
3399
3400             this.isAnimated = function() {
3401                 return isAnimated;
3402             };
3403
3404
3405             this.getStartTime = function() {
3406                 return startTime;
3407             };
3408
3409             this.runtimeAttributes = {};
3410
3411
3412             this.animate = function() {
3413                 if (this.isAnimated()) {
3414                     return false;
3415                 }
3416
3417                 this.currentFrame = 0;
3418
3419                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3420
3421                 Roo.lib.AnimMgr.registerElement(this);
3422             };
3423
3424
3425             this.stop = function(finish) {
3426                 if (finish) {
3427                     this.currentFrame = this.totalFrames;
3428                     this._onTween.fire();
3429                 }
3430                 Roo.lib.AnimMgr.stop(this);
3431             };
3432
3433             var onStart = function() {
3434                 this.onStart.fire();
3435
3436                 this.runtimeAttributes = {};
3437                 for (var attr in this.attributes) {
3438                     this.setRuntimeAttribute(attr);
3439                 }
3440
3441                 isAnimated = true;
3442                 actualFrames = 0;
3443                 startTime = new Date();
3444             };
3445
3446
3447             var onTween = function() {
3448                 var data = {
3449                     duration: new Date() - this.getStartTime(),
3450                     currentFrame: this.currentFrame
3451                 };
3452
3453                 data.toString = function() {
3454                     return (
3455                             'duration: ' + data.duration +
3456                             ', currentFrame: ' + data.currentFrame
3457                             );
3458                 };
3459
3460                 this.onTween.fire(data);
3461
3462                 var runtimeAttributes = this.runtimeAttributes;
3463
3464                 for (var attr in runtimeAttributes) {
3465                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3466                 }
3467
3468                 actualFrames += 1;
3469             };
3470
3471             var onComplete = function() {
3472                 var actual_duration = (new Date() - startTime) / 1000 ;
3473
3474                 var data = {
3475                     duration: actual_duration,
3476                     frames: actualFrames,
3477                     fps: actualFrames / actual_duration
3478                 };
3479
3480                 data.toString = function() {
3481                     return (
3482                             'duration: ' + data.duration +
3483                             ', frames: ' + data.frames +
3484                             ', fps: ' + data.fps
3485                             );
3486                 };
3487
3488                 isAnimated = false;
3489                 actualFrames = 0;
3490                 this.onComplete.fire(data);
3491             };
3492
3493
3494             this._onStart = new Roo.util.Event(this);
3495             this.onStart = new Roo.util.Event(this);
3496             this.onTween = new Roo.util.Event(this);
3497             this._onTween = new Roo.util.Event(this);
3498             this.onComplete = new Roo.util.Event(this);
3499             this._onComplete = new Roo.util.Event(this);
3500             this._onStart.addListener(onStart);
3501             this._onTween.addListener(onTween);
3502             this._onComplete.addListener(onComplete);
3503         }
3504     };
3505 })();
3506 /*
3507  * Portions of this file are based on pieces of Yahoo User Interface Library
3508  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3509  * YUI licensed under the BSD License:
3510  * http://developer.yahoo.net/yui/license.txt
3511  * <script type="text/javascript">
3512  *
3513  */
3514
3515 Roo.lib.AnimMgr = new function() {
3516
3517     var thread = null;
3518
3519
3520     var queue = [];
3521
3522
3523     var tweenCount = 0;
3524
3525
3526     this.fps = 1000;
3527
3528
3529     this.delay = 1;
3530
3531
3532     this.registerElement = function(tween) {
3533         queue[queue.length] = tween;
3534         tweenCount += 1;
3535         tween._onStart.fire();
3536         this.start();
3537     };
3538
3539
3540     this.unRegister = function(tween, index) {
3541         tween._onComplete.fire();
3542         index = index || getIndex(tween);
3543         if (index != -1) {
3544             queue.splice(index, 1);
3545         }
3546
3547         tweenCount -= 1;
3548         if (tweenCount <= 0) {
3549             this.stop();
3550         }
3551     };
3552
3553
3554     this.start = function() {
3555         if (thread === null) {
3556             thread = setInterval(this.run, this.delay);
3557         }
3558     };
3559
3560
3561     this.stop = function(tween) {
3562         if (!tween) {
3563             clearInterval(thread);
3564
3565             for (var i = 0, len = queue.length; i < len; ++i) {
3566                 if (queue[0].isAnimated()) {
3567                     this.unRegister(queue[0], 0);
3568                 }
3569             }
3570
3571             queue = [];
3572             thread = null;
3573             tweenCount = 0;
3574         }
3575         else {
3576             this.unRegister(tween);
3577         }
3578     };
3579
3580
3581     this.run = function() {
3582         for (var i = 0, len = queue.length; i < len; ++i) {
3583             var tween = queue[i];
3584             if (!tween || !tween.isAnimated()) {
3585                 continue;
3586             }
3587
3588             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3589             {
3590                 tween.currentFrame += 1;
3591
3592                 if (tween.useSeconds) {
3593                     correctFrame(tween);
3594                 }
3595                 tween._onTween.fire();
3596             }
3597             else {
3598                 Roo.lib.AnimMgr.stop(tween, i);
3599             }
3600         }
3601     };
3602
3603     var getIndex = function(anim) {
3604         for (var i = 0, len = queue.length; i < len; ++i) {
3605             if (queue[i] == anim) {
3606                 return i;
3607             }
3608         }
3609         return -1;
3610     };
3611
3612
3613     var correctFrame = function(tween) {
3614         var frames = tween.totalFrames;
3615         var frame = tween.currentFrame;
3616         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3617         var elapsed = (new Date() - tween.getStartTime());
3618         var tweak = 0;
3619
3620         if (elapsed < tween.duration * 1000) {
3621             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3622         } else {
3623             tweak = frames - (frame + 1);
3624         }
3625         if (tweak > 0 && isFinite(tweak)) {
3626             if (tween.currentFrame + tweak >= frames) {
3627                 tweak = frames - (frame + 1);
3628             }
3629
3630             tween.currentFrame += tweak;
3631         }
3632     };
3633 };
3634
3635     /*
3636  * Portions of this file are based on pieces of Yahoo User Interface Library
3637  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3638  * YUI licensed under the BSD License:
3639  * http://developer.yahoo.net/yui/license.txt
3640  * <script type="text/javascript">
3641  *
3642  */
3643 Roo.lib.Bezier = new function() {
3644
3645         this.getPosition = function(points, t) {
3646             var n = points.length;
3647             var tmp = [];
3648
3649             for (var i = 0; i < n; ++i) {
3650                 tmp[i] = [points[i][0], points[i][1]];
3651             }
3652
3653             for (var j = 1; j < n; ++j) {
3654                 for (i = 0; i < n - j; ++i) {
3655                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3656                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3657                 }
3658             }
3659
3660             return [ tmp[0][0], tmp[0][1] ];
3661
3662         };
3663     };/*
3664  * Portions of this file are based on pieces of Yahoo User Interface Library
3665  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3666  * YUI licensed under the BSD License:
3667  * http://developer.yahoo.net/yui/license.txt
3668  * <script type="text/javascript">
3669  *
3670  */
3671 (function() {
3672
3673     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3674         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3675     };
3676
3677     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3678
3679     var fly = Roo.lib.AnimBase.fly;
3680     var Y = Roo.lib;
3681     var superclass = Y.ColorAnim.superclass;
3682     var proto = Y.ColorAnim.prototype;
3683
3684     proto.toString = function() {
3685         var el = this.getEl();
3686         var id = el.id || el.tagName;
3687         return ("ColorAnim " + id);
3688     };
3689
3690     proto.patterns.color = /color$/i;
3691     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3692     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3693     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3694     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3695
3696
3697     proto.parseColor = function(s) {
3698         if (s.length == 3) {
3699             return s;
3700         }
3701
3702         var c = this.patterns.hex.exec(s);
3703         if (c && c.length == 4) {
3704             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3705         }
3706
3707         c = this.patterns.rgb.exec(s);
3708         if (c && c.length == 4) {
3709             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3710         }
3711
3712         c = this.patterns.hex3.exec(s);
3713         if (c && c.length == 4) {
3714             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3715         }
3716
3717         return null;
3718     };
3719     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743     proto.getAttribute = function(attr) {
3744         var el = this.getEl();
3745         if (this.patterns.color.test(attr)) {
3746             var val = fly(el).getStyle(attr);
3747
3748             if (this.patterns.transparent.test(val)) {
3749                 var parent = el.parentNode;
3750                 val = fly(parent).getStyle(attr);
3751
3752                 while (parent && this.patterns.transparent.test(val)) {
3753                     parent = parent.parentNode;
3754                     val = fly(parent).getStyle(attr);
3755                     if (parent.tagName.toUpperCase() == 'HTML') {
3756                         val = '#fff';
3757                     }
3758                 }
3759             }
3760         } else {
3761             val = superclass.getAttribute.call(this, attr);
3762         }
3763
3764         return val;
3765     };
3766
3767     proto.doMethod = function(attr, start, end) {
3768         var val;
3769
3770         if (this.patterns.color.test(attr)) {
3771             val = [];
3772             for (var i = 0, len = start.length; i < len; ++i) {
3773                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3774             }
3775
3776             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3777         }
3778         else {
3779             val = superclass.doMethod.call(this, attr, start, end);
3780         }
3781
3782         return val;
3783     };
3784
3785     proto.setRuntimeAttribute = function(attr) {
3786         superclass.setRuntimeAttribute.call(this, attr);
3787
3788         if (this.patterns.color.test(attr)) {
3789             var attributes = this.attributes;
3790             var start = this.parseColor(this.runtimeAttributes[attr].start);
3791             var end = this.parseColor(this.runtimeAttributes[attr].end);
3792
3793             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3794                 end = this.parseColor(attributes[attr].by);
3795
3796                 for (var i = 0, len = start.length; i < len; ++i) {
3797                     end[i] = start[i] + end[i];
3798                 }
3799             }
3800
3801             this.runtimeAttributes[attr].start = start;
3802             this.runtimeAttributes[attr].end = end;
3803         }
3804     };
3805 })();
3806
3807 /*
3808  * Portions of this file are based on pieces of Yahoo User Interface Library
3809  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3810  * YUI licensed under the BSD License:
3811  * http://developer.yahoo.net/yui/license.txt
3812  * <script type="text/javascript">
3813  *
3814  */
3815 Roo.lib.Easing = {
3816
3817
3818     easeNone: function (t, b, c, d) {
3819         return c * t / d + b;
3820     },
3821
3822
3823     easeIn: function (t, b, c, d) {
3824         return c * (t /= d) * t + b;
3825     },
3826
3827
3828     easeOut: function (t, b, c, d) {
3829         return -c * (t /= d) * (t - 2) + b;
3830     },
3831
3832
3833     easeBoth: function (t, b, c, d) {
3834         if ((t /= d / 2) < 1) {
3835             return c / 2 * t * t + b;
3836         }
3837
3838         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3839     },
3840
3841
3842     easeInStrong: function (t, b, c, d) {
3843         return c * (t /= d) * t * t * t + b;
3844     },
3845
3846
3847     easeOutStrong: function (t, b, c, d) {
3848         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3849     },
3850
3851
3852     easeBothStrong: function (t, b, c, d) {
3853         if ((t /= d / 2) < 1) {
3854             return c / 2 * t * t * t * t + b;
3855         }
3856
3857         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3858     },
3859
3860
3861
3862     elasticIn: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3882     },
3883
3884
3885     elasticOut: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889         if ((t /= d) == 1) {
3890             return b + c;
3891         }
3892         if (!p) {
3893             p = d * .3;
3894         }
3895
3896         if (!a || a < Math.abs(c)) {
3897             a = c;
3898             var s = p / 4;
3899         }
3900         else {
3901             var s = p / (2 * Math.PI) * Math.asin(c / a);
3902         }
3903
3904         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3905     },
3906
3907
3908     elasticBoth: function (t, b, c, d, a, p) {
3909         if (t == 0) {
3910             return b;
3911         }
3912
3913         if ((t /= d / 2) == 2) {
3914             return b + c;
3915         }
3916
3917         if (!p) {
3918             p = d * (.3 * 1.5);
3919         }
3920
3921         if (!a || a < Math.abs(c)) {
3922             a = c;
3923             var s = p / 4;
3924         }
3925         else {
3926             var s = p / (2 * Math.PI) * Math.asin(c / a);
3927         }
3928
3929         if (t < 1) {
3930             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3931                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3932         }
3933         return a * Math.pow(2, -10 * (t -= 1)) *
3934                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3935     },
3936
3937
3938
3939     backIn: function (t, b, c, d, s) {
3940         if (typeof s == 'undefined') {
3941             s = 1.70158;
3942         }
3943         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3944     },
3945
3946
3947     backOut: function (t, b, c, d, s) {
3948         if (typeof s == 'undefined') {
3949             s = 1.70158;
3950         }
3951         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3952     },
3953
3954
3955     backBoth: function (t, b, c, d, s) {
3956         if (typeof s == 'undefined') {
3957             s = 1.70158;
3958         }
3959
3960         if ((t /= d / 2 ) < 1) {
3961             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3962         }
3963         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3964     },
3965
3966
3967     bounceIn: function (t, b, c, d) {
3968         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3969     },
3970
3971
3972     bounceOut: function (t, b, c, d) {
3973         if ((t /= d) < (1 / 2.75)) {
3974             return c * (7.5625 * t * t) + b;
3975         } else if (t < (2 / 2.75)) {
3976             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3977         } else if (t < (2.5 / 2.75)) {
3978             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3979         }
3980         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3981     },
3982
3983
3984     bounceBoth: function (t, b, c, d) {
3985         if (t < d / 2) {
3986             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3987         }
3988         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3989     }
3990 };/*
3991  * Portions of this file are based on pieces of Yahoo User Interface Library
3992  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3993  * YUI licensed under the BSD License:
3994  * http://developer.yahoo.net/yui/license.txt
3995  * <script type="text/javascript">
3996  *
3997  */
3998     (function() {
3999         Roo.lib.Motion = function(el, attributes, duration, method) {
4000             if (el) {
4001                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4002             }
4003         };
4004
4005         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4006
4007
4008         var Y = Roo.lib;
4009         var superclass = Y.Motion.superclass;
4010         var proto = Y.Motion.prototype;
4011
4012         proto.toString = function() {
4013             var el = this.getEl();
4014             var id = el.id || el.tagName;
4015             return ("Motion " + id);
4016         };
4017
4018         proto.patterns.points = /^points$/i;
4019
4020         proto.setAttribute = function(attr, val, unit) {
4021             if (this.patterns.points.test(attr)) {
4022                 unit = unit || 'px';
4023                 superclass.setAttribute.call(this, 'left', val[0], unit);
4024                 superclass.setAttribute.call(this, 'top', val[1], unit);
4025             } else {
4026                 superclass.setAttribute.call(this, attr, val, unit);
4027             }
4028         };
4029
4030         proto.getAttribute = function(attr) {
4031             if (this.patterns.points.test(attr)) {
4032                 var val = [
4033                         superclass.getAttribute.call(this, 'left'),
4034                         superclass.getAttribute.call(this, 'top')
4035                         ];
4036             } else {
4037                 val = superclass.getAttribute.call(this, attr);
4038             }
4039
4040             return val;
4041         };
4042
4043         proto.doMethod = function(attr, start, end) {
4044             var val = null;
4045
4046             if (this.patterns.points.test(attr)) {
4047                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4048                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4049             } else {
4050                 val = superclass.doMethod.call(this, attr, start, end);
4051             }
4052             return val;
4053         };
4054
4055         proto.setRuntimeAttribute = function(attr) {
4056             if (this.patterns.points.test(attr)) {
4057                 var el = this.getEl();
4058                 var attributes = this.attributes;
4059                 var start;
4060                 var control = attributes['points']['control'] || [];
4061                 var end;
4062                 var i, len;
4063
4064                 if (control.length > 0 && !(control[0] instanceof Array)) {
4065                     control = [control];
4066                 } else {
4067                     var tmp = [];
4068                     for (i = 0,len = control.length; i < len; ++i) {
4069                         tmp[i] = control[i];
4070                     }
4071                     control = tmp;
4072                 }
4073
4074                 Roo.fly(el).position();
4075
4076                 if (isset(attributes['points']['from'])) {
4077                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4078                 }
4079                 else {
4080                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4081                 }
4082
4083                 start = this.getAttribute('points');
4084
4085
4086                 if (isset(attributes['points']['to'])) {
4087                     end = translateValues.call(this, attributes['points']['to'], start);
4088
4089                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4090                     for (i = 0,len = control.length; i < len; ++i) {
4091                         control[i] = translateValues.call(this, control[i], start);
4092                     }
4093
4094
4095                 } else if (isset(attributes['points']['by'])) {
4096                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4097
4098                     for (i = 0,len = control.length; i < len; ++i) {
4099                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4100                     }
4101                 }
4102
4103                 this.runtimeAttributes[attr] = [start];
4104
4105                 if (control.length > 0) {
4106                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4107                 }
4108
4109                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4110             }
4111             else {
4112                 superclass.setRuntimeAttribute.call(this, attr);
4113             }
4114         };
4115
4116         var translateValues = function(val, start) {
4117             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4118             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4119
4120             return val;
4121         };
4122
4123         var isset = function(prop) {
4124             return (typeof prop !== 'undefined');
4125         };
4126     })();
4127 /*
4128  * Portions of this file are based on pieces of Yahoo User Interface Library
4129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4130  * YUI licensed under the BSD License:
4131  * http://developer.yahoo.net/yui/license.txt
4132  * <script type="text/javascript">
4133  *
4134  */
4135     (function() {
4136         Roo.lib.Scroll = function(el, attributes, duration, method) {
4137             if (el) {
4138                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4139             }
4140         };
4141
4142         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4143
4144
4145         var Y = Roo.lib;
4146         var superclass = Y.Scroll.superclass;
4147         var proto = Y.Scroll.prototype;
4148
4149         proto.toString = function() {
4150             var el = this.getEl();
4151             var id = el.id || el.tagName;
4152             return ("Scroll " + id);
4153         };
4154
4155         proto.doMethod = function(attr, start, end) {
4156             var val = null;
4157
4158             if (attr == 'scroll') {
4159                 val = [
4160                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4161                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4162                         ];
4163
4164             } else {
4165                 val = superclass.doMethod.call(this, attr, start, end);
4166             }
4167             return val;
4168         };
4169
4170         proto.getAttribute = function(attr) {
4171             var val = null;
4172             var el = this.getEl();
4173
4174             if (attr == 'scroll') {
4175                 val = [ el.scrollLeft, el.scrollTop ];
4176             } else {
4177                 val = superclass.getAttribute.call(this, attr);
4178             }
4179
4180             return val;
4181         };
4182
4183         proto.setAttribute = function(attr, val, unit) {
4184             var el = this.getEl();
4185
4186             if (attr == 'scroll') {
4187                 el.scrollLeft = val[0];
4188                 el.scrollTop = val[1];
4189             } else {
4190                 superclass.setAttribute.call(this, attr, val, unit);
4191             }
4192         };
4193     })();
4194 /*
4195  * Based on:
4196  * Ext JS Library 1.1.1
4197  * Copyright(c) 2006-2007, Ext JS, LLC.
4198  *
4199  * Originally Released Under LGPL - original licence link has changed is not relivant.
4200  *
4201  * Fork - LGPL
4202  * <script type="text/javascript">
4203  */
4204
4205
4206 // nasty IE9 hack - what a pile of crap that is..
4207
4208  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4209     Range.prototype.createContextualFragment = function (html) {
4210         var doc = window.document;
4211         var container = doc.createElement("div");
4212         container.innerHTML = html;
4213         var frag = doc.createDocumentFragment(), n;
4214         while ((n = container.firstChild)) {
4215             frag.appendChild(n);
4216         }
4217         return frag;
4218     };
4219 }
4220
4221 /**
4222  * @class Roo.DomHelper
4223  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4224  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4225  * @singleton
4226  */
4227 Roo.DomHelper = function(){
4228     var tempTableEl = null;
4229     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4230     var tableRe = /^table|tbody|tr|td$/i;
4231     var xmlns = {};
4232     // build as innerHTML where available
4233     /** @ignore */
4234     var createHtml = function(o){
4235         if(typeof o == 'string'){
4236             return o;
4237         }
4238         var b = "";
4239         if(!o.tag){
4240             o.tag = "div";
4241         }
4242         b += "<" + o.tag;
4243         for(var attr in o){
4244             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4245             if(attr == "style"){
4246                 var s = o["style"];
4247                 if(typeof s == "function"){
4248                     s = s.call();
4249                 }
4250                 if(typeof s == "string"){
4251                     b += ' style="' + s + '"';
4252                 }else if(typeof s == "object"){
4253                     b += ' style="';
4254                     for(var key in s){
4255                         if(typeof s[key] != "function"){
4256                             b += key + ":" + s[key] + ";";
4257                         }
4258                     }
4259                     b += '"';
4260                 }
4261             }else{
4262                 if(attr == "cls"){
4263                     b += ' class="' + o["cls"] + '"';
4264                 }else if(attr == "htmlFor"){
4265                     b += ' for="' + o["htmlFor"] + '"';
4266                 }else{
4267                     b += " " + attr + '="' + o[attr] + '"';
4268                 }
4269             }
4270         }
4271         if(emptyTags.test(o.tag)){
4272             b += "/>";
4273         }else{
4274             b += ">";
4275             var cn = o.children || o.cn;
4276             if(cn){
4277                 //http://bugs.kde.org/show_bug.cgi?id=71506
4278                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4279                     for(var i = 0, len = cn.length; i < len; i++) {
4280                         b += createHtml(cn[i], b);
4281                     }
4282                 }else{
4283                     b += createHtml(cn, b);
4284                 }
4285             }
4286             if(o.html){
4287                 b += o.html;
4288             }
4289             b += "</" + o.tag + ">";
4290         }
4291         return b;
4292     };
4293
4294     // build as dom
4295     /** @ignore */
4296     var createDom = function(o, parentNode){
4297          
4298         // defininition craeted..
4299         var ns = false;
4300         if (o.ns && o.ns != 'html') {
4301                
4302             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4303                 xmlns[o.ns] = o.xmlns;
4304                 ns = o.xmlns;
4305             }
4306             if (typeof(xmlns[o.ns]) == 'undefined') {
4307                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4308             }
4309             ns = xmlns[o.ns];
4310         }
4311         
4312         
4313         if (typeof(o) == 'string') {
4314             return parentNode.appendChild(document.createTextNode(o));
4315         }
4316         o.tag = o.tag || div;
4317         if (o.ns && Roo.isIE) {
4318             ns = false;
4319             o.tag = o.ns + ':' + o.tag;
4320             
4321         }
4322         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4323         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4324         for(var attr in o){
4325             
4326             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4327                     attr == "style" || typeof o[attr] == "function") { continue; }
4328                     
4329             if(attr=="cls" && Roo.isIE){
4330                 el.className = o["cls"];
4331             }else{
4332                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4333                 else { 
4334                     el[attr] = o[attr];
4335                 }
4336             }
4337         }
4338         Roo.DomHelper.applyStyles(el, o.style);
4339         var cn = o.children || o.cn;
4340         if(cn){
4341             //http://bugs.kde.org/show_bug.cgi?id=71506
4342              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4343                 for(var i = 0, len = cn.length; i < len; i++) {
4344                     createDom(cn[i], el);
4345                 }
4346             }else{
4347                 createDom(cn, el);
4348             }
4349         }
4350         if(o.html){
4351             el.innerHTML = o.html;
4352         }
4353         if(parentNode){
4354            parentNode.appendChild(el);
4355         }
4356         return el;
4357     };
4358
4359     var ieTable = function(depth, s, h, e){
4360         tempTableEl.innerHTML = [s, h, e].join('');
4361         var i = -1, el = tempTableEl;
4362         while(++i < depth){
4363             el = el.firstChild;
4364         }
4365         return el;
4366     };
4367
4368     // kill repeat to save bytes
4369     var ts = '<table>',
4370         te = '</table>',
4371         tbs = ts+'<tbody>',
4372         tbe = '</tbody>'+te,
4373         trs = tbs + '<tr>',
4374         tre = '</tr>'+tbe;
4375
4376     /**
4377      * @ignore
4378      * Nasty code for IE's broken table implementation
4379      */
4380     var insertIntoTable = function(tag, where, el, html){
4381         if(!tempTableEl){
4382             tempTableEl = document.createElement('div');
4383         }
4384         var node;
4385         var before = null;
4386         if(tag == 'td'){
4387             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4388                 return;
4389             }
4390             if(where == 'beforebegin'){
4391                 before = el;
4392                 el = el.parentNode;
4393             } else{
4394                 before = el.nextSibling;
4395                 el = el.parentNode;
4396             }
4397             node = ieTable(4, trs, html, tre);
4398         }
4399         else if(tag == 'tr'){
4400             if(where == 'beforebegin'){
4401                 before = el;
4402                 el = el.parentNode;
4403                 node = ieTable(3, tbs, html, tbe);
4404             } else if(where == 'afterend'){
4405                 before = el.nextSibling;
4406                 el = el.parentNode;
4407                 node = ieTable(3, tbs, html, tbe);
4408             } else{ // INTO a TR
4409                 if(where == 'afterbegin'){
4410                     before = el.firstChild;
4411                 }
4412                 node = ieTable(4, trs, html, tre);
4413             }
4414         } else if(tag == 'tbody'){
4415             if(where == 'beforebegin'){
4416                 before = el;
4417                 el = el.parentNode;
4418                 node = ieTable(2, ts, html, te);
4419             } else if(where == 'afterend'){
4420                 before = el.nextSibling;
4421                 el = el.parentNode;
4422                 node = ieTable(2, ts, html, te);
4423             } else{
4424                 if(where == 'afterbegin'){
4425                     before = el.firstChild;
4426                 }
4427                 node = ieTable(3, tbs, html, tbe);
4428             }
4429         } else{ // TABLE
4430             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4431                 return;
4432             }
4433             if(where == 'afterbegin'){
4434                 before = el.firstChild;
4435             }
4436             node = ieTable(2, ts, html, te);
4437         }
4438         el.insertBefore(node, before);
4439         return node;
4440     };
4441
4442     return {
4443     /** True to force the use of DOM instead of html fragments @type Boolean */
4444     useDom : false,
4445
4446     /**
4447      * Returns the markup for the passed Element(s) config
4448      * @param {Object} o The Dom object spec (and children)
4449      * @return {String}
4450      */
4451     markup : function(o){
4452         return createHtml(o);
4453     },
4454
4455     /**
4456      * Applies a style specification to an element
4457      * @param {String/HTMLElement} el The element to apply styles to
4458      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4459      * a function which returns such a specification.
4460      */
4461     applyStyles : function(el, styles){
4462         if(styles){
4463            el = Roo.fly(el);
4464            if(typeof styles == "string"){
4465                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4466                var matches;
4467                while ((matches = re.exec(styles)) != null){
4468                    el.setStyle(matches[1], matches[2]);
4469                }
4470            }else if (typeof styles == "object"){
4471                for (var style in styles){
4472                   el.setStyle(style, styles[style]);
4473                }
4474            }else if (typeof styles == "function"){
4475                 Roo.DomHelper.applyStyles(el, styles.call());
4476            }
4477         }
4478     },
4479
4480     /**
4481      * Inserts an HTML fragment into the Dom
4482      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4483      * @param {HTMLElement} el The context element
4484      * @param {String} html The HTML fragmenet
4485      * @return {HTMLElement} The new node
4486      */
4487     insertHtml : function(where, el, html){
4488         where = where.toLowerCase();
4489         if(el.insertAdjacentHTML){
4490             if(tableRe.test(el.tagName)){
4491                 var rs;
4492                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4493                     return rs;
4494                 }
4495             }
4496             switch(where){
4497                 case "beforebegin":
4498                     el.insertAdjacentHTML('BeforeBegin', html);
4499                     return el.previousSibling;
4500                 case "afterbegin":
4501                     el.insertAdjacentHTML('AfterBegin', html);
4502                     return el.firstChild;
4503                 case "beforeend":
4504                     el.insertAdjacentHTML('BeforeEnd', html);
4505                     return el.lastChild;
4506                 case "afterend":
4507                     el.insertAdjacentHTML('AfterEnd', html);
4508                     return el.nextSibling;
4509             }
4510             throw 'Illegal insertion point -> "' + where + '"';
4511         }
4512         var range = el.ownerDocument.createRange();
4513         var frag;
4514         switch(where){
4515              case "beforebegin":
4516                 range.setStartBefore(el);
4517                 frag = range.createContextualFragment(html);
4518                 el.parentNode.insertBefore(frag, el);
4519                 return el.previousSibling;
4520              case "afterbegin":
4521                 if(el.firstChild){
4522                     range.setStartBefore(el.firstChild);
4523                     frag = range.createContextualFragment(html);
4524                     el.insertBefore(frag, el.firstChild);
4525                     return el.firstChild;
4526                 }else{
4527                     el.innerHTML = html;
4528                     return el.firstChild;
4529                 }
4530             case "beforeend":
4531                 if(el.lastChild){
4532                     range.setStartAfter(el.lastChild);
4533                     frag = range.createContextualFragment(html);
4534                     el.appendChild(frag);
4535                     return el.lastChild;
4536                 }else{
4537                     el.innerHTML = html;
4538                     return el.lastChild;
4539                 }
4540             case "afterend":
4541                 range.setStartAfter(el);
4542                 frag = range.createContextualFragment(html);
4543                 el.parentNode.insertBefore(frag, el.nextSibling);
4544                 return el.nextSibling;
4545             }
4546             throw 'Illegal insertion point -> "' + where + '"';
4547     },
4548
4549     /**
4550      * Creates new Dom element(s) and inserts them before el
4551      * @param {String/HTMLElement/Element} el The context element
4552      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4553      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4554      * @return {HTMLElement/Roo.Element} The new node
4555      */
4556     insertBefore : function(el, o, returnElement){
4557         return this.doInsert(el, o, returnElement, "beforeBegin");
4558     },
4559
4560     /**
4561      * Creates new Dom element(s) and inserts them after el
4562      * @param {String/HTMLElement/Element} el The context element
4563      * @param {Object} o The Dom object spec (and children)
4564      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4565      * @return {HTMLElement/Roo.Element} The new node
4566      */
4567     insertAfter : function(el, o, returnElement){
4568         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4569     },
4570
4571     /**
4572      * Creates new Dom element(s) and inserts them as the first child of el
4573      * @param {String/HTMLElement/Element} el The context element
4574      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4575      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4576      * @return {HTMLElement/Roo.Element} The new node
4577      */
4578     insertFirst : function(el, o, returnElement){
4579         return this.doInsert(el, o, returnElement, "afterBegin");
4580     },
4581
4582     // private
4583     doInsert : function(el, o, returnElement, pos, sibling){
4584         el = Roo.getDom(el);
4585         var newNode;
4586         if(this.useDom || o.ns){
4587             newNode = createDom(o, null);
4588             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4589         }else{
4590             var html = createHtml(o);
4591             newNode = this.insertHtml(pos, el, html);
4592         }
4593         return returnElement ? Roo.get(newNode, true) : newNode;
4594     },
4595
4596     /**
4597      * Creates new Dom element(s) and appends them to el
4598      * @param {String/HTMLElement/Element} el The context element
4599      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4600      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4601      * @return {HTMLElement/Roo.Element} The new node
4602      */
4603     append : function(el, o, returnElement){
4604         el = Roo.getDom(el);
4605         var newNode;
4606         if(this.useDom || o.ns){
4607             newNode = createDom(o, null);
4608             el.appendChild(newNode);
4609         }else{
4610             var html = createHtml(o);
4611             newNode = this.insertHtml("beforeEnd", el, html);
4612         }
4613         return returnElement ? Roo.get(newNode, true) : newNode;
4614     },
4615
4616     /**
4617      * Creates new Dom element(s) and overwrites the contents of el with them
4618      * @param {String/HTMLElement/Element} el The context element
4619      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4620      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4621      * @return {HTMLElement/Roo.Element} The new node
4622      */
4623     overwrite : function(el, o, returnElement){
4624         el = Roo.getDom(el);
4625         if (o.ns) {
4626           
4627             while (el.childNodes.length) {
4628                 el.removeChild(el.firstChild);
4629             }
4630             createDom(o, el);
4631         } else {
4632             el.innerHTML = createHtml(o);   
4633         }
4634         
4635         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4636     },
4637
4638     /**
4639      * Creates a new Roo.DomHelper.Template from the Dom object spec
4640      * @param {Object} o The Dom object spec (and children)
4641      * @return {Roo.DomHelper.Template} The new template
4642      */
4643     createTemplate : function(o){
4644         var html = createHtml(o);
4645         return new Roo.Template(html);
4646     }
4647     };
4648 }();
4649 /*
4650  * Based on:
4651  * Ext JS Library 1.1.1
4652  * Copyright(c) 2006-2007, Ext JS, LLC.
4653  *
4654  * Originally Released Under LGPL - original licence link has changed is not relivant.
4655  *
4656  * Fork - LGPL
4657  * <script type="text/javascript">
4658  */
4659  
4660 /**
4661 * @class Roo.Template
4662 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4663 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4664 * Usage:
4665 <pre><code>
4666 var t = new Roo.Template({
4667     html :  '&lt;div name="{id}"&gt;' + 
4668         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4669         '&lt;/div&gt;',
4670     myformat: function (value, allValues) {
4671         return 'XX' + value;
4672     }
4673 });
4674 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4675 </code></pre>
4676 * For more information see this blog post with examples:
4677 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4678      - Create Elements using DOM, HTML fragments and Templates</a>. 
4679 * @constructor
4680 * @param {Object} cfg - Configuration object.
4681 */
4682 Roo.Template = function(cfg){
4683     // BC!
4684     if(cfg instanceof Array){
4685         cfg = cfg.join("");
4686     }else if(arguments.length > 1){
4687         cfg = Array.prototype.join.call(arguments, "");
4688     }
4689     
4690     
4691     if (typeof(cfg) == 'object') {
4692         Roo.apply(this,cfg)
4693     } else {
4694         // bc
4695         this.html = cfg;
4696     }
4697     if (this.url) {
4698         this.load();
4699     }
4700     
4701 };
4702 Roo.Template.prototype = {
4703     
4704     /**
4705      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4706      */
4707     onLoad : false,
4708     
4709     
4710     /**
4711      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4712      *                    it should be fixed so that template is observable...
4713      */
4714     url : false,
4715     /**
4716      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4717      */
4718     html : '',
4719     
4720     
4721     compiled : false,
4722     loaded : false,
4723     /**
4724      * Returns an HTML fragment of this template with the specified values applied.
4725      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4726      * @return {String} The HTML fragment
4727      */
4728     
4729    
4730     
4731     applyTemplate : function(values){
4732         //Roo.log(["applyTemplate", values]);
4733         try {
4734            
4735             if(this.compiled){
4736                 return this.compiled(values);
4737             }
4738             var useF = this.disableFormats !== true;
4739             var fm = Roo.util.Format, tpl = this;
4740             var fn = function(m, name, format, args){
4741                 if(format && useF){
4742                     if(format.substr(0, 5) == "this."){
4743                         return tpl.call(format.substr(5), values[name], values);
4744                     }else{
4745                         if(args){
4746                             // quoted values are required for strings in compiled templates, 
4747                             // but for non compiled we need to strip them
4748                             // quoted reversed for jsmin
4749                             var re = /^\s*['"](.*)["']\s*$/;
4750                             args = args.split(',');
4751                             for(var i = 0, len = args.length; i < len; i++){
4752                                 args[i] = args[i].replace(re, "$1");
4753                             }
4754                             args = [values[name]].concat(args);
4755                         }else{
4756                             args = [values[name]];
4757                         }
4758                         return fm[format].apply(fm, args);
4759                     }
4760                 }else{
4761                     return values[name] !== undefined ? values[name] : "";
4762                 }
4763             };
4764             return this.html.replace(this.re, fn);
4765         } catch (e) {
4766             Roo.log(e);
4767             throw e;
4768         }
4769          
4770     },
4771     
4772     loading : false,
4773       
4774     load : function ()
4775     {
4776          
4777         if (this.loading) {
4778             return;
4779         }
4780         var _t = this;
4781         
4782         this.loading = true;
4783         this.compiled = false;
4784         
4785         var cx = new Roo.data.Connection();
4786         cx.request({
4787             url : this.url,
4788             method : 'GET',
4789             success : function (response) {
4790                 _t.loading = false;
4791                 _t.url = false;
4792                 
4793                 _t.set(response.responseText,true);
4794                 _t.loaded = true;
4795                 if (_t.onLoad) {
4796                     _t.onLoad();
4797                 }
4798              },
4799             failure : function(response) {
4800                 Roo.log("Template failed to load from " + _t.url);
4801                 _t.loading = false;
4802             }
4803         });
4804     },
4805
4806     /**
4807      * Sets the HTML used as the template and optionally compiles it.
4808      * @param {String} html
4809      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4810      * @return {Roo.Template} this
4811      */
4812     set : function(html, compile){
4813         this.html = html;
4814         this.compiled = false;
4815         if(compile){
4816             this.compile();
4817         }
4818         return this;
4819     },
4820     
4821     /**
4822      * True to disable format functions (defaults to false)
4823      * @type Boolean
4824      */
4825     disableFormats : false,
4826     
4827     /**
4828     * The regular expression used to match template variables 
4829     * @type RegExp
4830     * @property 
4831     */
4832     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4833     
4834     /**
4835      * Compiles the template into an internal function, eliminating the RegEx overhead.
4836      * @return {Roo.Template} this
4837      */
4838     compile : function(){
4839         var fm = Roo.util.Format;
4840         var useF = this.disableFormats !== true;
4841         var sep = Roo.isGecko ? "+" : ",";
4842         var fn = function(m, name, format, args){
4843             if(format && useF){
4844                 args = args ? ',' + args : "";
4845                 if(format.substr(0, 5) != "this."){
4846                     format = "fm." + format + '(';
4847                 }else{
4848                     format = 'this.call("'+ format.substr(5) + '", ';
4849                     args = ", values";
4850                 }
4851             }else{
4852                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4853             }
4854             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4855         };
4856         var body;
4857         // branched to use + in gecko and [].join() in others
4858         if(Roo.isGecko){
4859             body = "this.compiled = function(values){ return '" +
4860                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4861                     "';};";
4862         }else{
4863             body = ["this.compiled = function(values){ return ['"];
4864             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4865             body.push("'].join('');};");
4866             body = body.join('');
4867         }
4868         /**
4869          * eval:var:values
4870          * eval:var:fm
4871          */
4872         eval(body);
4873         return this;
4874     },
4875     
4876     // private function used to call members
4877     call : function(fnName, value, allValues){
4878         return this[fnName](value, allValues);
4879     },
4880     
4881     /**
4882      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4883      * @param {String/HTMLElement/Roo.Element} el The context element
4884      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4885      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4886      * @return {HTMLElement/Roo.Element} The new node or Element
4887      */
4888     insertFirst: function(el, values, returnElement){
4889         return this.doInsert('afterBegin', el, values, returnElement);
4890     },
4891
4892     /**
4893      * Applies the supplied values to the template and inserts the new node(s) before el.
4894      * @param {String/HTMLElement/Roo.Element} el The context element
4895      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4896      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4897      * @return {HTMLElement/Roo.Element} The new node or Element
4898      */
4899     insertBefore: function(el, values, returnElement){
4900         return this.doInsert('beforeBegin', el, values, returnElement);
4901     },
4902
4903     /**
4904      * Applies the supplied values to the template and inserts the new node(s) after el.
4905      * @param {String/HTMLElement/Roo.Element} el The context element
4906      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4907      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4908      * @return {HTMLElement/Roo.Element} The new node or Element
4909      */
4910     insertAfter : function(el, values, returnElement){
4911         return this.doInsert('afterEnd', el, values, returnElement);
4912     },
4913     
4914     /**
4915      * Applies the supplied values to the template and appends the new node(s) to el.
4916      * @param {String/HTMLElement/Roo.Element} el The context element
4917      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4918      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4919      * @return {HTMLElement/Roo.Element} The new node or Element
4920      */
4921     append : function(el, values, returnElement){
4922         return this.doInsert('beforeEnd', el, values, returnElement);
4923     },
4924
4925     doInsert : function(where, el, values, returnEl){
4926         el = Roo.getDom(el);
4927         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4928         return returnEl ? Roo.get(newNode, true) : newNode;
4929     },
4930
4931     /**
4932      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4933      * @param {String/HTMLElement/Roo.Element} el The context element
4934      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4935      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4936      * @return {HTMLElement/Roo.Element} The new node or Element
4937      */
4938     overwrite : function(el, values, returnElement){
4939         el = Roo.getDom(el);
4940         el.innerHTML = this.applyTemplate(values);
4941         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4942     }
4943 };
4944 /**
4945  * Alias for {@link #applyTemplate}
4946  * @method
4947  */
4948 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4949
4950 // backwards compat
4951 Roo.DomHelper.Template = Roo.Template;
4952
4953 /**
4954  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4955  * @param {String/HTMLElement} el A DOM element or its id
4956  * @returns {Roo.Template} The created template
4957  * @static
4958  */
4959 Roo.Template.from = function(el){
4960     el = Roo.getDom(el);
4961     return new Roo.Template(el.value || el.innerHTML);
4962 };/*
4963  * Based on:
4964  * Ext JS Library 1.1.1
4965  * Copyright(c) 2006-2007, Ext JS, LLC.
4966  *
4967  * Originally Released Under LGPL - original licence link has changed is not relivant.
4968  *
4969  * Fork - LGPL
4970  * <script type="text/javascript">
4971  */
4972  
4973
4974 /*
4975  * This is code is also distributed under MIT license for use
4976  * with jQuery and prototype JavaScript libraries.
4977  */
4978 /**
4979  * @class Roo.DomQuery
4980 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4981 <p>
4982 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4983
4984 <p>
4985 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4986 </p>
4987 <h4>Element Selectors:</h4>
4988 <ul class="list">
4989     <li> <b>*</b> any element</li>
4990     <li> <b>E</b> an element with the tag E</li>
4991     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4992     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4993     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4994     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4995 </ul>
4996 <h4>Attribute Selectors:</h4>
4997 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4998 <ul class="list">
4999     <li> <b>E[foo]</b> has an attribute "foo"</li>
5000     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5001     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5002     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5003     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5004     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5005     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5006 </ul>
5007 <h4>Pseudo Classes:</h4>
5008 <ul class="list">
5009     <li> <b>E:first-child</b> E is the first child of its parent</li>
5010     <li> <b>E:last-child</b> E is the last child of its parent</li>
5011     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
5012     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5013     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5014     <li> <b>E:only-child</b> E is the only child of its parent</li>
5015     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
5016     <li> <b>E:first</b> the first E in the resultset</li>
5017     <li> <b>E:last</b> the last E in the resultset</li>
5018     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5019     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5020     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5021     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5022     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5023     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5024     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5025     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5026     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5027 </ul>
5028 <h4>CSS Value Selectors:</h4>
5029 <ul class="list">
5030     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5031     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5032     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5033     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5034     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5035     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5036 </ul>
5037  * @singleton
5038  */
5039 Roo.DomQuery = function(){
5040     var cache = {}, simpleCache = {}, valueCache = {};
5041     var nonSpace = /\S/;
5042     var trimRe = /^\s+|\s+$/g;
5043     var tplRe = /\{(\d+)\}/g;
5044     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5045     var tagTokenRe = /^(#)?([\w-\*]+)/;
5046     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5047
5048     function child(p, index){
5049         var i = 0;
5050         var n = p.firstChild;
5051         while(n){
5052             if(n.nodeType == 1){
5053                if(++i == index){
5054                    return n;
5055                }
5056             }
5057             n = n.nextSibling;
5058         }
5059         return null;
5060     };
5061
5062     function next(n){
5063         while((n = n.nextSibling) && n.nodeType != 1);
5064         return n;
5065     };
5066
5067     function prev(n){
5068         while((n = n.previousSibling) && n.nodeType != 1);
5069         return n;
5070     };
5071
5072     function children(d){
5073         var n = d.firstChild, ni = -1;
5074             while(n){
5075                 var nx = n.nextSibling;
5076                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5077                     d.removeChild(n);
5078                 }else{
5079                     n.nodeIndex = ++ni;
5080                 }
5081                 n = nx;
5082             }
5083             return this;
5084         };
5085
5086     function byClassName(c, a, v){
5087         if(!v){
5088             return c;
5089         }
5090         var r = [], ri = -1, cn;
5091         for(var i = 0, ci; ci = c[i]; i++){
5092             
5093             
5094             if((' '+
5095                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5096                  +' ').indexOf(v) != -1){
5097                 r[++ri] = ci;
5098             }
5099         }
5100         return r;
5101     };
5102
5103     function attrValue(n, attr){
5104         if(!n.tagName && typeof n.length != "undefined"){
5105             n = n[0];
5106         }
5107         if(!n){
5108             return null;
5109         }
5110         if(attr == "for"){
5111             return n.htmlFor;
5112         }
5113         if(attr == "class" || attr == "className"){
5114             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5115         }
5116         return n.getAttribute(attr) || n[attr];
5117
5118     };
5119
5120     function getNodes(ns, mode, tagName){
5121         var result = [], ri = -1, cs;
5122         if(!ns){
5123             return result;
5124         }
5125         tagName = tagName || "*";
5126         if(typeof ns.getElementsByTagName != "undefined"){
5127             ns = [ns];
5128         }
5129         if(!mode){
5130             for(var i = 0, ni; ni = ns[i]; i++){
5131                 cs = ni.getElementsByTagName(tagName);
5132                 for(var j = 0, ci; ci = cs[j]; j++){
5133                     result[++ri] = ci;
5134                 }
5135             }
5136         }else if(mode == "/" || mode == ">"){
5137             var utag = tagName.toUpperCase();
5138             for(var i = 0, ni, cn; ni = ns[i]; i++){
5139                 cn = ni.children || ni.childNodes;
5140                 for(var j = 0, cj; cj = cn[j]; j++){
5141                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5142                         result[++ri] = cj;
5143                     }
5144                 }
5145             }
5146         }else if(mode == "+"){
5147             var utag = tagName.toUpperCase();
5148             for(var i = 0, n; n = ns[i]; i++){
5149                 while((n = n.nextSibling) && n.nodeType != 1);
5150                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5151                     result[++ri] = n;
5152                 }
5153             }
5154         }else if(mode == "~"){
5155             for(var i = 0, n; n = ns[i]; i++){
5156                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5157                 if(n){
5158                     result[++ri] = n;
5159                 }
5160             }
5161         }
5162         return result;
5163     };
5164
5165     function concat(a, b){
5166         if(b.slice){
5167             return a.concat(b);
5168         }
5169         for(var i = 0, l = b.length; i < l; i++){
5170             a[a.length] = b[i];
5171         }
5172         return a;
5173     }
5174
5175     function byTag(cs, tagName){
5176         if(cs.tagName || cs == document){
5177             cs = [cs];
5178         }
5179         if(!tagName){
5180             return cs;
5181         }
5182         var r = [], ri = -1;
5183         tagName = tagName.toLowerCase();
5184         for(var i = 0, ci; ci = cs[i]; i++){
5185             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5186                 r[++ri] = ci;
5187             }
5188         }
5189         return r;
5190     };
5191
5192     function byId(cs, attr, id){
5193         if(cs.tagName || cs == document){
5194             cs = [cs];
5195         }
5196         if(!id){
5197             return cs;
5198         }
5199         var r = [], ri = -1;
5200         for(var i = 0,ci; ci = cs[i]; i++){
5201             if(ci && ci.id == id){
5202                 r[++ri] = ci;
5203                 return r;
5204             }
5205         }
5206         return r;
5207     };
5208
5209     function byAttribute(cs, attr, value, op, custom){
5210         var r = [], ri = -1, st = custom=="{";
5211         var f = Roo.DomQuery.operators[op];
5212         for(var i = 0, ci; ci = cs[i]; i++){
5213             var a;
5214             if(st){
5215                 a = Roo.DomQuery.getStyle(ci, attr);
5216             }
5217             else if(attr == "class" || attr == "className"){
5218                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5219             }else if(attr == "for"){
5220                 a = ci.htmlFor;
5221             }else if(attr == "href"){
5222                 a = ci.getAttribute("href", 2);
5223             }else{
5224                 a = ci.getAttribute(attr);
5225             }
5226             if((f && f(a, value)) || (!f && a)){
5227                 r[++ri] = ci;
5228             }
5229         }
5230         return r;
5231     };
5232
5233     function byPseudo(cs, name, value){
5234         return Roo.DomQuery.pseudos[name](cs, value);
5235     };
5236
5237     // This is for IE MSXML which does not support expandos.
5238     // IE runs the same speed using setAttribute, however FF slows way down
5239     // and Safari completely fails so they need to continue to use expandos.
5240     var isIE = window.ActiveXObject ? true : false;
5241
5242     // this eval is stop the compressor from
5243     // renaming the variable to something shorter
5244     
5245     /** eval:var:batch */
5246     var batch = 30803; 
5247
5248     var key = 30803;
5249
5250     function nodupIEXml(cs){
5251         var d = ++key;
5252         cs[0].setAttribute("_nodup", d);
5253         var r = [cs[0]];
5254         for(var i = 1, len = cs.length; i < len; i++){
5255             var c = cs[i];
5256             if(!c.getAttribute("_nodup") != d){
5257                 c.setAttribute("_nodup", d);
5258                 r[r.length] = c;
5259             }
5260         }
5261         for(var i = 0, len = cs.length; i < len; i++){
5262             cs[i].removeAttribute("_nodup");
5263         }
5264         return r;
5265     }
5266
5267     function nodup(cs){
5268         if(!cs){
5269             return [];
5270         }
5271         var len = cs.length, c, i, r = cs, cj, ri = -1;
5272         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5273             return cs;
5274         }
5275         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5276             return nodupIEXml(cs);
5277         }
5278         var d = ++key;
5279         cs[0]._nodup = d;
5280         for(i = 1; c = cs[i]; i++){
5281             if(c._nodup != d){
5282                 c._nodup = d;
5283             }else{
5284                 r = [];
5285                 for(var j = 0; j < i; j++){
5286                     r[++ri] = cs[j];
5287                 }
5288                 for(j = i+1; cj = cs[j]; j++){
5289                     if(cj._nodup != d){
5290                         cj._nodup = d;
5291                         r[++ri] = cj;
5292                     }
5293                 }
5294                 return r;
5295             }
5296         }
5297         return r;
5298     }
5299
5300     function quickDiffIEXml(c1, c2){
5301         var d = ++key;
5302         for(var i = 0, len = c1.length; i < len; i++){
5303             c1[i].setAttribute("_qdiff", d);
5304         }
5305         var r = [];
5306         for(var i = 0, len = c2.length; i < len; i++){
5307             if(c2[i].getAttribute("_qdiff") != d){
5308                 r[r.length] = c2[i];
5309             }
5310         }
5311         for(var i = 0, len = c1.length; i < len; i++){
5312            c1[i].removeAttribute("_qdiff");
5313         }
5314         return r;
5315     }
5316
5317     function quickDiff(c1, c2){
5318         var len1 = c1.length;
5319         if(!len1){
5320             return c2;
5321         }
5322         if(isIE && c1[0].selectSingleNode){
5323             return quickDiffIEXml(c1, c2);
5324         }
5325         var d = ++key;
5326         for(var i = 0; i < len1; i++){
5327             c1[i]._qdiff = d;
5328         }
5329         var r = [];
5330         for(var i = 0, len = c2.length; i < len; i++){
5331             if(c2[i]._qdiff != d){
5332                 r[r.length] = c2[i];
5333             }
5334         }
5335         return r;
5336     }
5337
5338     function quickId(ns, mode, root, id){
5339         if(ns == root){
5340            var d = root.ownerDocument || root;
5341            return d.getElementById(id);
5342         }
5343         ns = getNodes(ns, mode, "*");
5344         return byId(ns, null, id);
5345     }
5346
5347     return {
5348         getStyle : function(el, name){
5349             return Roo.fly(el).getStyle(name);
5350         },
5351         /**
5352          * Compiles a selector/xpath query into a reusable function. The returned function
5353          * takes one parameter "root" (optional), which is the context node from where the query should start.
5354          * @param {String} selector The selector/xpath query
5355          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5356          * @return {Function}
5357          */
5358         compile : function(path, type){
5359             type = type || "select";
5360             
5361             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5362             var q = path, mode, lq;
5363             var tk = Roo.DomQuery.matchers;
5364             var tklen = tk.length;
5365             var mm;
5366
5367             // accept leading mode switch
5368             var lmode = q.match(modeRe);
5369             if(lmode && lmode[1]){
5370                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5371                 q = q.replace(lmode[1], "");
5372             }
5373             // strip leading slashes
5374             while(path.substr(0, 1)=="/"){
5375                 path = path.substr(1);
5376             }
5377
5378             while(q && lq != q){
5379                 lq = q;
5380                 var tm = q.match(tagTokenRe);
5381                 if(type == "select"){
5382                     if(tm){
5383                         if(tm[1] == "#"){
5384                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5385                         }else{
5386                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5387                         }
5388                         q = q.replace(tm[0], "");
5389                     }else if(q.substr(0, 1) != '@'){
5390                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5391                     }
5392                 }else{
5393                     if(tm){
5394                         if(tm[1] == "#"){
5395                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5396                         }else{
5397                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5398                         }
5399                         q = q.replace(tm[0], "");
5400                     }
5401                 }
5402                 while(!(mm = q.match(modeRe))){
5403                     var matched = false;
5404                     for(var j = 0; j < tklen; j++){
5405                         var t = tk[j];
5406                         var m = q.match(t.re);
5407                         if(m){
5408                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5409                                                     return m[i];
5410                                                 });
5411                             q = q.replace(m[0], "");
5412                             matched = true;
5413                             break;
5414                         }
5415                     }
5416                     // prevent infinite loop on bad selector
5417                     if(!matched){
5418                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5419                     }
5420                 }
5421                 if(mm[1]){
5422                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5423                     q = q.replace(mm[1], "");
5424                 }
5425             }
5426             fn[fn.length] = "return nodup(n);\n}";
5427             
5428              /** 
5429               * list of variables that need from compression as they are used by eval.
5430              *  eval:var:batch 
5431              *  eval:var:nodup
5432              *  eval:var:byTag
5433              *  eval:var:ById
5434              *  eval:var:getNodes
5435              *  eval:var:quickId
5436              *  eval:var:mode
5437              *  eval:var:root
5438              *  eval:var:n
5439              *  eval:var:byClassName
5440              *  eval:var:byPseudo
5441              *  eval:var:byAttribute
5442              *  eval:var:attrValue
5443              * 
5444              **/ 
5445             eval(fn.join(""));
5446             return f;
5447         },
5448
5449         /**
5450          * Selects a group of elements.
5451          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5452          * @param {Node} root (optional) The start of the query (defaults to document).
5453          * @return {Array}
5454          */
5455         select : function(path, root, type){
5456             if(!root || root == document){
5457                 root = document;
5458             }
5459             if(typeof root == "string"){
5460                 root = document.getElementById(root);
5461             }
5462             var paths = path.split(",");
5463             var results = [];
5464             for(var i = 0, len = paths.length; i < len; i++){
5465                 var p = paths[i].replace(trimRe, "");
5466                 if(!cache[p]){
5467                     cache[p] = Roo.DomQuery.compile(p);
5468                     if(!cache[p]){
5469                         throw p + " is not a valid selector";
5470                     }
5471                 }
5472                 var result = cache[p](root);
5473                 if(result && result != document){
5474                     results = results.concat(result);
5475                 }
5476             }
5477             if(paths.length > 1){
5478                 return nodup(results);
5479             }
5480             return results;
5481         },
5482
5483         /**
5484          * Selects a single element.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @return {Element}
5488          */
5489         selectNode : function(path, root){
5490             return Roo.DomQuery.select(path, root)[0];
5491         },
5492
5493         /**
5494          * Selects the value of a node, optionally replacing null with the defaultValue.
5495          * @param {String} selector The selector/xpath query
5496          * @param {Node} root (optional) The start of the query (defaults to document).
5497          * @param {String} defaultValue
5498          */
5499         selectValue : function(path, root, defaultValue){
5500             path = path.replace(trimRe, "");
5501             if(!valueCache[path]){
5502                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5503             }
5504             var n = valueCache[path](root);
5505             n = n[0] ? n[0] : n;
5506             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5507             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5508         },
5509
5510         /**
5511          * Selects the value of a node, parsing integers and floats.
5512          * @param {String} selector The selector/xpath query
5513          * @param {Node} root (optional) The start of the query (defaults to document).
5514          * @param {Number} defaultValue
5515          * @return {Number}
5516          */
5517         selectNumber : function(path, root, defaultValue){
5518             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5519             return parseFloat(v);
5520         },
5521
5522         /**
5523          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5524          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5525          * @param {String} selector The simple selector to test
5526          * @return {Boolean}
5527          */
5528         is : function(el, ss){
5529             if(typeof el == "string"){
5530                 el = document.getElementById(el);
5531             }
5532             var isArray = (el instanceof Array);
5533             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5534             return isArray ? (result.length == el.length) : (result.length > 0);
5535         },
5536
5537         /**
5538          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5539          * @param {Array} el An array of elements to filter
5540          * @param {String} selector The simple selector to test
5541          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5542          * the selector instead of the ones that match
5543          * @return {Array}
5544          */
5545         filter : function(els, ss, nonMatches){
5546             ss = ss.replace(trimRe, "");
5547             if(!simpleCache[ss]){
5548                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5549             }
5550             var result = simpleCache[ss](els);
5551             return nonMatches ? quickDiff(result, els) : result;
5552         },
5553
5554         /**
5555          * Collection of matching regular expressions and code snippets.
5556          */
5557         matchers : [{
5558                 re: /^\.([\w-]+)/,
5559                 select: 'n = byClassName(n, null, " {1} ");'
5560             }, {
5561                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5562                 select: 'n = byPseudo(n, "{1}", "{2}");'
5563             },{
5564                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5565                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5566             }, {
5567                 re: /^#([\w-]+)/,
5568                 select: 'n = byId(n, null, "{1}");'
5569             },{
5570                 re: /^@([\w-]+)/,
5571                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5572             }
5573         ],
5574
5575         /**
5576          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5577          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5578          */
5579         operators : {
5580             "=" : function(a, v){
5581                 return a == v;
5582             },
5583             "!=" : function(a, v){
5584                 return a != v;
5585             },
5586             "^=" : function(a, v){
5587                 return a && a.substr(0, v.length) == v;
5588             },
5589             "$=" : function(a, v){
5590                 return a && a.substr(a.length-v.length) == v;
5591             },
5592             "*=" : function(a, v){
5593                 return a && a.indexOf(v) !== -1;
5594             },
5595             "%=" : function(a, v){
5596                 return (a % v) == 0;
5597             },
5598             "|=" : function(a, v){
5599                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5600             },
5601             "~=" : function(a, v){
5602                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5603             }
5604         },
5605
5606         /**
5607          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5608          * and the argument (if any) supplied in the selector.
5609          */
5610         pseudos : {
5611             "first-child" : function(c){
5612                 var r = [], ri = -1, n;
5613                 for(var i = 0, ci; ci = n = c[i]; i++){
5614                     while((n = n.previousSibling) && n.nodeType != 1);
5615                     if(!n){
5616                         r[++ri] = ci;
5617                     }
5618                 }
5619                 return r;
5620             },
5621
5622             "last-child" : function(c){
5623                 var r = [], ri = -1, n;
5624                 for(var i = 0, ci; ci = n = c[i]; i++){
5625                     while((n = n.nextSibling) && n.nodeType != 1);
5626                     if(!n){
5627                         r[++ri] = ci;
5628                     }
5629                 }
5630                 return r;
5631             },
5632
5633             "nth-child" : function(c, a) {
5634                 var r = [], ri = -1;
5635                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5636                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5637                 for(var i = 0, n; n = c[i]; i++){
5638                     var pn = n.parentNode;
5639                     if (batch != pn._batch) {
5640                         var j = 0;
5641                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5642                             if(cn.nodeType == 1){
5643                                cn.nodeIndex = ++j;
5644                             }
5645                         }
5646                         pn._batch = batch;
5647                     }
5648                     if (f == 1) {
5649                         if (l == 0 || n.nodeIndex == l){
5650                             r[++ri] = n;
5651                         }
5652                     } else if ((n.nodeIndex + l) % f == 0){
5653                         r[++ri] = n;
5654                     }
5655                 }
5656
5657                 return r;
5658             },
5659
5660             "only-child" : function(c){
5661                 var r = [], ri = -1;;
5662                 for(var i = 0, ci; ci = c[i]; i++){
5663                     if(!prev(ci) && !next(ci)){
5664                         r[++ri] = ci;
5665                     }
5666                 }
5667                 return r;
5668             },
5669
5670             "empty" : function(c){
5671                 var r = [], ri = -1;
5672                 for(var i = 0, ci; ci = c[i]; i++){
5673                     var cns = ci.childNodes, j = 0, cn, empty = true;
5674                     while(cn = cns[j]){
5675                         ++j;
5676                         if(cn.nodeType == 1 || cn.nodeType == 3){
5677                             empty = false;
5678                             break;
5679                         }
5680                     }
5681                     if(empty){
5682                         r[++ri] = ci;
5683                     }
5684                 }
5685                 return r;
5686             },
5687
5688             "contains" : function(c, v){
5689                 var r = [], ri = -1;
5690                 for(var i = 0, ci; ci = c[i]; i++){
5691                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5692                         r[++ri] = ci;
5693                     }
5694                 }
5695                 return r;
5696             },
5697
5698             "nodeValue" : function(c, v){
5699                 var r = [], ri = -1;
5700                 for(var i = 0, ci; ci = c[i]; i++){
5701                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5702                         r[++ri] = ci;
5703                     }
5704                 }
5705                 return r;
5706             },
5707
5708             "checked" : function(c){
5709                 var r = [], ri = -1;
5710                 for(var i = 0, ci; ci = c[i]; i++){
5711                     if(ci.checked == true){
5712                         r[++ri] = ci;
5713                     }
5714                 }
5715                 return r;
5716             },
5717
5718             "not" : function(c, ss){
5719                 return Roo.DomQuery.filter(c, ss, true);
5720             },
5721
5722             "odd" : function(c){
5723                 return this["nth-child"](c, "odd");
5724             },
5725
5726             "even" : function(c){
5727                 return this["nth-child"](c, "even");
5728             },
5729
5730             "nth" : function(c, a){
5731                 return c[a-1] || [];
5732             },
5733
5734             "first" : function(c){
5735                 return c[0] || [];
5736             },
5737
5738             "last" : function(c){
5739                 return c[c.length-1] || [];
5740             },
5741
5742             "has" : function(c, ss){
5743                 var s = Roo.DomQuery.select;
5744                 var r = [], ri = -1;
5745                 for(var i = 0, ci; ci = c[i]; i++){
5746                     if(s(ss, ci).length > 0){
5747                         r[++ri] = ci;
5748                     }
5749                 }
5750                 return r;
5751             },
5752
5753             "next" : function(c, ss){
5754                 var is = Roo.DomQuery.is;
5755                 var r = [], ri = -1;
5756                 for(var i = 0, ci; ci = c[i]; i++){
5757                     var n = next(ci);
5758                     if(n && is(n, ss)){
5759                         r[++ri] = ci;
5760                     }
5761                 }
5762                 return r;
5763             },
5764
5765             "prev" : function(c, ss){
5766                 var is = Roo.DomQuery.is;
5767                 var r = [], ri = -1;
5768                 for(var i = 0, ci; ci = c[i]; i++){
5769                     var n = prev(ci);
5770                     if(n && is(n, ss)){
5771                         r[++ri] = ci;
5772                     }
5773                 }
5774                 return r;
5775             }
5776         }
5777     };
5778 }();
5779
5780 /**
5781  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5782  * @param {String} path The selector/xpath query
5783  * @param {Node} root (optional) The start of the query (defaults to document).
5784  * @return {Array}
5785  * @member Roo
5786  * @method query
5787  */
5788 Roo.query = Roo.DomQuery.select;
5789 /*
5790  * Based on:
5791  * Ext JS Library 1.1.1
5792  * Copyright(c) 2006-2007, Ext JS, LLC.
5793  *
5794  * Originally Released Under LGPL - original licence link has changed is not relivant.
5795  *
5796  * Fork - LGPL
5797  * <script type="text/javascript">
5798  */
5799
5800 /**
5801  * @class Roo.util.Observable
5802  * Base class that provides a common interface for publishing events. Subclasses are expected to
5803  * to have a property "events" with all the events defined.<br>
5804  * For example:
5805  * <pre><code>
5806  Employee = function(name){
5807     this.name = name;
5808     this.addEvents({
5809         "fired" : true,
5810         "quit" : true
5811     });
5812  }
5813  Roo.extend(Employee, Roo.util.Observable);
5814 </code></pre>
5815  * @param {Object} config properties to use (incuding events / listeners)
5816  */
5817
5818 Roo.util.Observable = function(cfg){
5819     
5820     cfg = cfg|| {};
5821     this.addEvents(cfg.events || {});
5822     if (cfg.events) {
5823         delete cfg.events; // make sure
5824     }
5825      
5826     Roo.apply(this, cfg);
5827     
5828     if(this.listeners){
5829         this.on(this.listeners);
5830         delete this.listeners;
5831     }
5832 };
5833 Roo.util.Observable.prototype = {
5834     /** 
5835  * @cfg {Object} listeners  list of events and functions to call for this object, 
5836  * For example :
5837  * <pre><code>
5838     listeners :  { 
5839        'click' : function(e) {
5840            ..... 
5841         } ,
5842         .... 
5843     } 
5844   </code></pre>
5845  */
5846     
5847     
5848     /**
5849      * Fires the specified event with the passed parameters (minus the event name).
5850      * @param {String} eventName
5851      * @param {Object...} args Variable number of parameters are passed to handlers
5852      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5853      */
5854     fireEvent : function(){
5855         var ce = this.events[arguments[0].toLowerCase()];
5856         if(typeof ce == "object"){
5857             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5858         }else{
5859             return true;
5860         }
5861     },
5862
5863     // private
5864     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5865
5866     /**
5867      * Appends an event handler to this component
5868      * @param {String}   eventName The type of event to listen for
5869      * @param {Function} handler The method the event invokes
5870      * @param {Object}   scope (optional) The scope in which to execute the handler
5871      * function. The handler function's "this" context.
5872      * @param {Object}   options (optional) An object containing handler configuration
5873      * properties. This may contain any of the following properties:<ul>
5874      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5875      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5876      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5877      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5878      * by the specified number of milliseconds. If the event fires again within that time, the original
5879      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5880      * </ul><br>
5881      * <p>
5882      * <b>Combining Options</b><br>
5883      * Using the options argument, it is possible to combine different types of listeners:<br>
5884      * <br>
5885      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5886                 <pre><code>
5887                 el.on('click', this.onClick, this, {
5888                         single: true,
5889                 delay: 100,
5890                 forumId: 4
5891                 });
5892                 </code></pre>
5893      * <p>
5894      * <b>Attaching multiple handlers in 1 call</b><br>
5895      * The method also allows for a single argument to be passed which is a config object containing properties
5896      * which specify multiple handlers.
5897      * <pre><code>
5898                 el.on({
5899                         'click': {
5900                         fn: this.onClick,
5901                         scope: this,
5902                         delay: 100
5903                 }, 
5904                 'mouseover': {
5905                         fn: this.onMouseOver,
5906                         scope: this
5907                 },
5908                 'mouseout': {
5909                         fn: this.onMouseOut,
5910                         scope: this
5911                 }
5912                 });
5913                 </code></pre>
5914      * <p>
5915      * Or a shorthand syntax which passes the same scope object to all handlers:
5916         <pre><code>
5917                 el.on({
5918                         'click': this.onClick,
5919                 'mouseover': this.onMouseOver,
5920                 'mouseout': this.onMouseOut,
5921                 scope: this
5922                 });
5923                 </code></pre>
5924      */
5925     addListener : function(eventName, fn, scope, o){
5926         if(typeof eventName == "object"){
5927             o = eventName;
5928             for(var e in o){
5929                 if(this.filterOptRe.test(e)){
5930                     continue;
5931                 }
5932                 if(typeof o[e] == "function"){
5933                     // shared options
5934                     this.addListener(e, o[e], o.scope,  o);
5935                 }else{
5936                     // individual options
5937                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5938                 }
5939             }
5940             return;
5941         }
5942         o = (!o || typeof o == "boolean") ? {} : o;
5943         eventName = eventName.toLowerCase();
5944         var ce = this.events[eventName] || true;
5945         if(typeof ce == "boolean"){
5946             ce = new Roo.util.Event(this, eventName);
5947             this.events[eventName] = ce;
5948         }
5949         ce.addListener(fn, scope, o);
5950     },
5951
5952     /**
5953      * Removes a listener
5954      * @param {String}   eventName     The type of event to listen for
5955      * @param {Function} handler        The handler to remove
5956      * @param {Object}   scope  (optional) The scope (this object) for the handler
5957      */
5958     removeListener : function(eventName, fn, scope){
5959         var ce = this.events[eventName.toLowerCase()];
5960         if(typeof ce == "object"){
5961             ce.removeListener(fn, scope);
5962         }
5963     },
5964
5965     /**
5966      * Removes all listeners for this object
5967      */
5968     purgeListeners : function(){
5969         for(var evt in this.events){
5970             if(typeof this.events[evt] == "object"){
5971                  this.events[evt].clearListeners();
5972             }
5973         }
5974     },
5975
5976     relayEvents : function(o, events){
5977         var createHandler = function(ename){
5978             return function(){
5979                  
5980                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5981             };
5982         };
5983         for(var i = 0, len = events.length; i < len; i++){
5984             var ename = events[i];
5985             if(!this.events[ename]){
5986                 this.events[ename] = true;
5987             };
5988             o.on(ename, createHandler(ename), this);
5989         }
5990     },
5991
5992     /**
5993      * Used to define events on this Observable
5994      * @param {Object} object The object with the events defined
5995      */
5996     addEvents : function(o){
5997         if(!this.events){
5998             this.events = {};
5999         }
6000         Roo.applyIf(this.events, o);
6001     },
6002
6003     /**
6004      * Checks to see if this object has any listeners for a specified event
6005      * @param {String} eventName The name of the event to check for
6006      * @return {Boolean} True if the event is being listened for, else false
6007      */
6008     hasListener : function(eventName){
6009         var e = this.events[eventName];
6010         return typeof e == "object" && e.listeners.length > 0;
6011     }
6012 };
6013 /**
6014  * Appends an event handler to this element (shorthand for addListener)
6015  * @param {String}   eventName     The type of event to listen for
6016  * @param {Function} handler        The method the event invokes
6017  * @param {Object}   scope (optional) The scope in which to execute the handler
6018  * function. The handler function's "this" context.
6019  * @param {Object}   options  (optional)
6020  * @method
6021  */
6022 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6023 /**
6024  * Removes a listener (shorthand for removeListener)
6025  * @param {String}   eventName     The type of event to listen for
6026  * @param {Function} handler        The handler to remove
6027  * @param {Object}   scope  (optional) The scope (this object) for the handler
6028  * @method
6029  */
6030 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6031
6032 /**
6033  * Starts capture on the specified Observable. All events will be passed
6034  * to the supplied function with the event name + standard signature of the event
6035  * <b>before</b> the event is fired. If the supplied function returns false,
6036  * the event will not fire.
6037  * @param {Observable} o The Observable to capture
6038  * @param {Function} fn The function to call
6039  * @param {Object} scope (optional) The scope (this object) for the fn
6040  * @static
6041  */
6042 Roo.util.Observable.capture = function(o, fn, scope){
6043     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6044 };
6045
6046 /**
6047  * Removes <b>all</b> added captures from the Observable.
6048  * @param {Observable} o The Observable to release
6049  * @static
6050  */
6051 Roo.util.Observable.releaseCapture = function(o){
6052     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6053 };
6054
6055 (function(){
6056
6057     var createBuffered = function(h, o, scope){
6058         var task = new Roo.util.DelayedTask();
6059         return function(){
6060             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6061         };
6062     };
6063
6064     var createSingle = function(h, e, fn, scope){
6065         return function(){
6066             e.removeListener(fn, scope);
6067             return h.apply(scope, arguments);
6068         };
6069     };
6070
6071     var createDelayed = function(h, o, scope){
6072         return function(){
6073             var args = Array.prototype.slice.call(arguments, 0);
6074             setTimeout(function(){
6075                 h.apply(scope, args);
6076             }, o.delay || 10);
6077         };
6078     };
6079
6080     Roo.util.Event = function(obj, name){
6081         this.name = name;
6082         this.obj = obj;
6083         this.listeners = [];
6084     };
6085
6086     Roo.util.Event.prototype = {
6087         addListener : function(fn, scope, options){
6088             var o = options || {};
6089             scope = scope || this.obj;
6090             if(!this.isListening(fn, scope)){
6091                 var l = {fn: fn, scope: scope, options: o};
6092                 var h = fn;
6093                 if(o.delay){
6094                     h = createDelayed(h, o, scope);
6095                 }
6096                 if(o.single){
6097                     h = createSingle(h, this, fn, scope);
6098                 }
6099                 if(o.buffer){
6100                     h = createBuffered(h, o, scope);
6101                 }
6102                 l.fireFn = h;
6103                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6104                     this.listeners.push(l);
6105                 }else{
6106                     this.listeners = this.listeners.slice(0);
6107                     this.listeners.push(l);
6108                 }
6109             }
6110         },
6111
6112         findListener : function(fn, scope){
6113             scope = scope || this.obj;
6114             var ls = this.listeners;
6115             for(var i = 0, len = ls.length; i < len; i++){
6116                 var l = ls[i];
6117                 if(l.fn == fn && l.scope == scope){
6118                     return i;
6119                 }
6120             }
6121             return -1;
6122         },
6123
6124         isListening : function(fn, scope){
6125             return this.findListener(fn, scope) != -1;
6126         },
6127
6128         removeListener : function(fn, scope){
6129             var index;
6130             if((index = this.findListener(fn, scope)) != -1){
6131                 if(!this.firing){
6132                     this.listeners.splice(index, 1);
6133                 }else{
6134                     this.listeners = this.listeners.slice(0);
6135                     this.listeners.splice(index, 1);
6136                 }
6137                 return true;
6138             }
6139             return false;
6140         },
6141
6142         clearListeners : function(){
6143             this.listeners = [];
6144         },
6145
6146         fire : function(){
6147             var ls = this.listeners, scope, len = ls.length;
6148             if(len > 0){
6149                 this.firing = true;
6150                 var args = Array.prototype.slice.call(arguments, 0);                
6151                 for(var i = 0; i < len; i++){
6152                     var l = ls[i];
6153                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6154                         this.firing = false;
6155                         return false;
6156                     }
6157                 }
6158                 this.firing = false;
6159             }
6160             return true;
6161         }
6162     };
6163 })();/*
6164  * RooJS Library 
6165  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6166  *
6167  * Licence LGPL 
6168  *
6169  */
6170  
6171 /**
6172  * @class Roo.Document
6173  * @extends Roo.util.Observable
6174  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6175  * 
6176  * @param {Object} config the methods and properties of the 'base' class for the application.
6177  * 
6178  *  Generic Page handler - implement this to start your app..
6179  * 
6180  * eg.
6181  *  MyProject = new Roo.Document({
6182         events : {
6183             'load' : true // your events..
6184         },
6185         listeners : {
6186             'ready' : function() {
6187                 // fired on Roo.onReady()
6188             }
6189         }
6190  * 
6191  */
6192 Roo.Document = function(cfg) {
6193      
6194     this.addEvents({ 
6195         'ready' : true
6196     });
6197     Roo.util.Observable.call(this,cfg);
6198     
6199     var _this = this;
6200     
6201     Roo.onReady(function() {
6202         _this.fireEvent('ready');
6203     },null,false);
6204     
6205     
6206 }
6207
6208 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6209  * Based on:
6210  * Ext JS Library 1.1.1
6211  * Copyright(c) 2006-2007, Ext JS, LLC.
6212  *
6213  * Originally Released Under LGPL - original licence link has changed is not relivant.
6214  *
6215  * Fork - LGPL
6216  * <script type="text/javascript">
6217  */
6218
6219 /**
6220  * @class Roo.EventManager
6221  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6222  * several useful events directly.
6223  * See {@link Roo.EventObject} for more details on normalized event objects.
6224  * @singleton
6225  */
6226 Roo.EventManager = function(){
6227     var docReadyEvent, docReadyProcId, docReadyState = false;
6228     var resizeEvent, resizeTask, textEvent, textSize;
6229     var E = Roo.lib.Event;
6230     var D = Roo.lib.Dom;
6231
6232     
6233     
6234
6235     var fireDocReady = function(){
6236         if(!docReadyState){
6237             docReadyState = true;
6238             Roo.isReady = true;
6239             if(docReadyProcId){
6240                 clearInterval(docReadyProcId);
6241             }
6242             if(Roo.isGecko || Roo.isOpera) {
6243                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6244             }
6245             if(Roo.isIE){
6246                 var defer = document.getElementById("ie-deferred-loader");
6247                 if(defer){
6248                     defer.onreadystatechange = null;
6249                     defer.parentNode.removeChild(defer);
6250                 }
6251             }
6252             if(docReadyEvent){
6253                 docReadyEvent.fire();
6254                 docReadyEvent.clearListeners();
6255             }
6256         }
6257     };
6258     
6259     var initDocReady = function(){
6260         docReadyEvent = new Roo.util.Event();
6261         if(Roo.isGecko || Roo.isOpera) {
6262             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6263         }else if(Roo.isIE){
6264             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6265             var defer = document.getElementById("ie-deferred-loader");
6266             defer.onreadystatechange = function(){
6267                 if(this.readyState == "complete"){
6268                     fireDocReady();
6269                 }
6270             };
6271         }else if(Roo.isSafari){ 
6272             docReadyProcId = setInterval(function(){
6273                 var rs = document.readyState;
6274                 if(rs == "complete") {
6275                     fireDocReady();     
6276                  }
6277             }, 10);
6278         }
6279         // no matter what, make sure it fires on load
6280         E.on(window, "load", fireDocReady);
6281     };
6282
6283     var createBuffered = function(h, o){
6284         var task = new Roo.util.DelayedTask(h);
6285         return function(e){
6286             // create new event object impl so new events don't wipe out properties
6287             e = new Roo.EventObjectImpl(e);
6288             task.delay(o.buffer, h, null, [e]);
6289         };
6290     };
6291
6292     var createSingle = function(h, el, ename, fn){
6293         return function(e){
6294             Roo.EventManager.removeListener(el, ename, fn);
6295             h(e);
6296         };
6297     };
6298
6299     var createDelayed = function(h, o){
6300         return function(e){
6301             // create new event object impl so new events don't wipe out properties
6302             e = new Roo.EventObjectImpl(e);
6303             setTimeout(function(){
6304                 h(e);
6305             }, o.delay || 10);
6306         };
6307     };
6308     var transitionEndVal = false;
6309     
6310     var transitionEnd = function()
6311     {
6312         if (transitionEndVal) {
6313             return transitionEndVal;
6314         }
6315         var el = document.createElement('div');
6316
6317         var transEndEventNames = {
6318             WebkitTransition : 'webkitTransitionEnd',
6319             MozTransition    : 'transitionend',
6320             OTransition      : 'oTransitionEnd otransitionend',
6321             transition       : 'transitionend'
6322         };
6323     
6324         for (var name in transEndEventNames) {
6325             if (el.style[name] !== undefined) {
6326                 transitionEndVal = transEndEventNames[name];
6327                 return  transitionEndVal ;
6328             }
6329         }
6330     }
6331     
6332   
6333
6334     var listen = function(element, ename, opt, fn, scope)
6335     {
6336         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6337         fn = fn || o.fn; scope = scope || o.scope;
6338         var el = Roo.getDom(element);
6339         
6340         
6341         if(!el){
6342             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6343         }
6344         
6345         if (ename == 'transitionend') {
6346             ename = transitionEnd();
6347         }
6348         var h = function(e){
6349             e = Roo.EventObject.setEvent(e);
6350             var t;
6351             if(o.delegate){
6352                 t = e.getTarget(o.delegate, el);
6353                 if(!t){
6354                     return;
6355                 }
6356             }else{
6357                 t = e.target;
6358             }
6359             if(o.stopEvent === true){
6360                 e.stopEvent();
6361             }
6362             if(o.preventDefault === true){
6363                e.preventDefault();
6364             }
6365             if(o.stopPropagation === true){
6366                 e.stopPropagation();
6367             }
6368
6369             if(o.normalized === false){
6370                 e = e.browserEvent;
6371             }
6372
6373             fn.call(scope || el, e, t, o);
6374         };
6375         if(o.delay){
6376             h = createDelayed(h, o);
6377         }
6378         if(o.single){
6379             h = createSingle(h, el, ename, fn);
6380         }
6381         if(o.buffer){
6382             h = createBuffered(h, o);
6383         }
6384         
6385         fn._handlers = fn._handlers || [];
6386         
6387         
6388         fn._handlers.push([Roo.id(el), ename, h]);
6389         
6390         
6391          
6392         E.on(el, ename, h); // this adds the actuall listener to the object..
6393         
6394         
6395         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6396             el.addEventListener("DOMMouseScroll", h, false);
6397             E.on(window, 'unload', function(){
6398                 el.removeEventListener("DOMMouseScroll", h, false);
6399             });
6400         }
6401         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6402             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6403         }
6404         return h;
6405     };
6406
6407     var stopListening = function(el, ename, fn){
6408         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6409         if(hds){
6410             for(var i = 0, len = hds.length; i < len; i++){
6411                 var h = hds[i];
6412                 if(h[0] == id && h[1] == ename){
6413                     hd = h[2];
6414                     hds.splice(i, 1);
6415                     break;
6416                 }
6417             }
6418         }
6419         E.un(el, ename, hd);
6420         el = Roo.getDom(el);
6421         if(ename == "mousewheel" && el.addEventListener){
6422             el.removeEventListener("DOMMouseScroll", hd, false);
6423         }
6424         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6425             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6426         }
6427     };
6428
6429     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6430     
6431     var pub = {
6432         
6433         
6434         /** 
6435          * Fix for doc tools
6436          * @scope Roo.EventManager
6437          */
6438         
6439         
6440         /** 
6441          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6442          * object with a Roo.EventObject
6443          * @param {Function} fn        The method the event invokes
6444          * @param {Object}   scope    An object that becomes the scope of the handler
6445          * @param {boolean}  override If true, the obj passed in becomes
6446          *                             the execution scope of the listener
6447          * @return {Function} The wrapped function
6448          * @deprecated
6449          */
6450         wrap : function(fn, scope, override){
6451             return function(e){
6452                 Roo.EventObject.setEvent(e);
6453                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6454             };
6455         },
6456         
6457         /**
6458      * Appends an event handler to an element (shorthand for addListener)
6459      * @param {String/HTMLElement}   element        The html element or id to assign the
6460      * @param {String}   eventName The type of event to listen for
6461      * @param {Function} handler The method the event invokes
6462      * @param {Object}   scope (optional) The scope in which to execute the handler
6463      * function. The handler function's "this" context.
6464      * @param {Object}   options (optional) An object containing handler configuration
6465      * properties. This may contain any of the following properties:<ul>
6466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6469      * <li>preventDefault {Boolean} True to prevent the default action</li>
6470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6475      * by the specified number of milliseconds. If the event fires again within that time, the original
6476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6477      * </ul><br>
6478      * <p>
6479      * <b>Combining Options</b><br>
6480      * Using the options argument, it is possible to combine different types of listeners:<br>
6481      * <br>
6482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6483      * Code:<pre><code>
6484 el.on('click', this.onClick, this, {
6485     single: true,
6486     delay: 100,
6487     stopEvent : true,
6488     forumId: 4
6489 });</code></pre>
6490      * <p>
6491      * <b>Attaching multiple handlers in 1 call</b><br>
6492       * The method also allows for a single argument to be passed which is a config object containing properties
6493      * which specify multiple handlers.
6494      * <p>
6495      * Code:<pre><code>
6496 el.on({
6497     'click' : {
6498         fn: this.onClick
6499         scope: this,
6500         delay: 100
6501     },
6502     'mouseover' : {
6503         fn: this.onMouseOver
6504         scope: this
6505     },
6506     'mouseout' : {
6507         fn: this.onMouseOut
6508         scope: this
6509     }
6510 });</code></pre>
6511      * <p>
6512      * Or a shorthand syntax:<br>
6513      * Code:<pre><code>
6514 el.on({
6515     'click' : this.onClick,
6516     'mouseover' : this.onMouseOver,
6517     'mouseout' : this.onMouseOut
6518     scope: this
6519 });</code></pre>
6520      */
6521         addListener : function(element, eventName, fn, scope, options){
6522             if(typeof eventName == "object"){
6523                 var o = eventName;
6524                 for(var e in o){
6525                     if(propRe.test(e)){
6526                         continue;
6527                     }
6528                     if(typeof o[e] == "function"){
6529                         // shared options
6530                         listen(element, e, o, o[e], o.scope);
6531                     }else{
6532                         // individual options
6533                         listen(element, e, o[e]);
6534                     }
6535                 }
6536                 return;
6537             }
6538             return listen(element, eventName, options, fn, scope);
6539         },
6540         
6541         /**
6542          * Removes an event handler
6543          *
6544          * @param {String/HTMLElement}   element        The id or html element to remove the 
6545          *                             event from
6546          * @param {String}   eventName     The type of event
6547          * @param {Function} fn
6548          * @return {Boolean} True if a listener was actually removed
6549          */
6550         removeListener : function(element, eventName, fn){
6551             return stopListening(element, eventName, fn);
6552         },
6553         
6554         /**
6555          * Fires when the document is ready (before onload and before images are loaded). Can be 
6556          * accessed shorthanded Roo.onReady().
6557          * @param {Function} fn        The method the event invokes
6558          * @param {Object}   scope    An  object that becomes the scope of the handler
6559          * @param {boolean}  options
6560          */
6561         onDocumentReady : function(fn, scope, options){
6562             if(docReadyState){ // if it already fired
6563                 docReadyEvent.addListener(fn, scope, options);
6564                 docReadyEvent.fire();
6565                 docReadyEvent.clearListeners();
6566                 return;
6567             }
6568             if(!docReadyEvent){
6569                 initDocReady();
6570             }
6571             docReadyEvent.addListener(fn, scope, options);
6572         },
6573         
6574         /**
6575          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6576          * @param {Function} fn        The method the event invokes
6577          * @param {Object}   scope    An object that becomes the scope of the handler
6578          * @param {boolean}  options
6579          */
6580         onWindowResize : function(fn, scope, options){
6581             if(!resizeEvent){
6582                 resizeEvent = new Roo.util.Event();
6583                 resizeTask = new Roo.util.DelayedTask(function(){
6584                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6585                 });
6586                 E.on(window, "resize", function(){
6587                     if(Roo.isIE){
6588                         resizeTask.delay(50);
6589                     }else{
6590                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6591                     }
6592                 });
6593             }
6594             resizeEvent.addListener(fn, scope, options);
6595         },
6596
6597         /**
6598          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6599          * @param {Function} fn        The method the event invokes
6600          * @param {Object}   scope    An object that becomes the scope of the handler
6601          * @param {boolean}  options
6602          */
6603         onTextResize : function(fn, scope, options){
6604             if(!textEvent){
6605                 textEvent = new Roo.util.Event();
6606                 var textEl = new Roo.Element(document.createElement('div'));
6607                 textEl.dom.className = 'x-text-resize';
6608                 textEl.dom.innerHTML = 'X';
6609                 textEl.appendTo(document.body);
6610                 textSize = textEl.dom.offsetHeight;
6611                 setInterval(function(){
6612                     if(textEl.dom.offsetHeight != textSize){
6613                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6614                     }
6615                 }, this.textResizeInterval);
6616             }
6617             textEvent.addListener(fn, scope, options);
6618         },
6619
6620         /**
6621          * Removes the passed window resize listener.
6622          * @param {Function} fn        The method the event invokes
6623          * @param {Object}   scope    The scope of handler
6624          */
6625         removeResizeListener : function(fn, scope){
6626             if(resizeEvent){
6627                 resizeEvent.removeListener(fn, scope);
6628             }
6629         },
6630
6631         // private
6632         fireResize : function(){
6633             if(resizeEvent){
6634                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6635             }   
6636         },
6637         /**
6638          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6639          */
6640         ieDeferSrc : false,
6641         /**
6642          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6643          */
6644         textResizeInterval : 50
6645     };
6646     
6647     /**
6648      * Fix for doc tools
6649      * @scopeAlias pub=Roo.EventManager
6650      */
6651     
6652      /**
6653      * Appends an event handler to an element (shorthand for addListener)
6654      * @param {String/HTMLElement}   element        The html element or id to assign the
6655      * @param {String}   eventName The type of event to listen for
6656      * @param {Function} handler The method the event invokes
6657      * @param {Object}   scope (optional) The scope in which to execute the handler
6658      * function. The handler function's "this" context.
6659      * @param {Object}   options (optional) An object containing handler configuration
6660      * properties. This may contain any of the following properties:<ul>
6661      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6662      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6663      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6664      * <li>preventDefault {Boolean} True to prevent the default action</li>
6665      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6666      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6667      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6668      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6669      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6670      * by the specified number of milliseconds. If the event fires again within that time, the original
6671      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6672      * </ul><br>
6673      * <p>
6674      * <b>Combining Options</b><br>
6675      * Using the options argument, it is possible to combine different types of listeners:<br>
6676      * <br>
6677      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6678      * Code:<pre><code>
6679 el.on('click', this.onClick, this, {
6680     single: true,
6681     delay: 100,
6682     stopEvent : true,
6683     forumId: 4
6684 });</code></pre>
6685      * <p>
6686      * <b>Attaching multiple handlers in 1 call</b><br>
6687       * The method also allows for a single argument to be passed which is a config object containing properties
6688      * which specify multiple handlers.
6689      * <p>
6690      * Code:<pre><code>
6691 el.on({
6692     'click' : {
6693         fn: this.onClick
6694         scope: this,
6695         delay: 100
6696     },
6697     'mouseover' : {
6698         fn: this.onMouseOver
6699         scope: this
6700     },
6701     'mouseout' : {
6702         fn: this.onMouseOut
6703         scope: this
6704     }
6705 });</code></pre>
6706      * <p>
6707      * Or a shorthand syntax:<br>
6708      * Code:<pre><code>
6709 el.on({
6710     'click' : this.onClick,
6711     'mouseover' : this.onMouseOver,
6712     'mouseout' : this.onMouseOut
6713     scope: this
6714 });</code></pre>
6715      */
6716     pub.on = pub.addListener;
6717     pub.un = pub.removeListener;
6718
6719     pub.stoppedMouseDownEvent = new Roo.util.Event();
6720     return pub;
6721 }();
6722 /**
6723   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6724   * @param {Function} fn        The method the event invokes
6725   * @param {Object}   scope    An  object that becomes the scope of the handler
6726   * @param {boolean}  override If true, the obj passed in becomes
6727   *                             the execution scope of the listener
6728   * @member Roo
6729   * @method onReady
6730  */
6731 Roo.onReady = Roo.EventManager.onDocumentReady;
6732
6733 Roo.onReady(function(){
6734     var bd = Roo.get(document.body);
6735     if(!bd){ return; }
6736
6737     var cls = [
6738             Roo.isIE ? "roo-ie"
6739             : Roo.isIE11 ? "roo-ie11"
6740             : Roo.isEdge ? "roo-edge"
6741             : Roo.isGecko ? "roo-gecko"
6742             : Roo.isOpera ? "roo-opera"
6743             : Roo.isSafari ? "roo-safari" : ""];
6744
6745     if(Roo.isMac){
6746         cls.push("roo-mac");
6747     }
6748     if(Roo.isLinux){
6749         cls.push("roo-linux");
6750     }
6751     if(Roo.isIOS){
6752         cls.push("roo-ios");
6753     }
6754     if(Roo.isTouch){
6755         cls.push("roo-touch");
6756     }
6757     if(Roo.isBorderBox){
6758         cls.push('roo-border-box');
6759     }
6760     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6761         var p = bd.dom.parentNode;
6762         if(p){
6763             p.className += ' roo-strict';
6764         }
6765     }
6766     bd.addClass(cls.join(' '));
6767 });
6768
6769 /**
6770  * @class Roo.EventObject
6771  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6772  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6773  * Example:
6774  * <pre><code>
6775  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6776     e.preventDefault();
6777     var target = e.getTarget();
6778     ...
6779  }
6780  var myDiv = Roo.get("myDiv");
6781  myDiv.on("click", handleClick);
6782  //or
6783  Roo.EventManager.on("myDiv", 'click', handleClick);
6784  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6785  </code></pre>
6786  * @singleton
6787  */
6788 Roo.EventObject = function(){
6789     
6790     var E = Roo.lib.Event;
6791     
6792     // safari keypress events for special keys return bad keycodes
6793     var safariKeys = {
6794         63234 : 37, // left
6795         63235 : 39, // right
6796         63232 : 38, // up
6797         63233 : 40, // down
6798         63276 : 33, // page up
6799         63277 : 34, // page down
6800         63272 : 46, // delete
6801         63273 : 36, // home
6802         63275 : 35  // end
6803     };
6804
6805     // normalize button clicks
6806     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6807                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6808
6809     Roo.EventObjectImpl = function(e){
6810         if(e){
6811             this.setEvent(e.browserEvent || e);
6812         }
6813     };
6814     Roo.EventObjectImpl.prototype = {
6815         /**
6816          * Used to fix doc tools.
6817          * @scope Roo.EventObject.prototype
6818          */
6819             
6820
6821         
6822         
6823         /** The normal browser event */
6824         browserEvent : null,
6825         /** The button pressed in a mouse event */
6826         button : -1,
6827         /** True if the shift key was down during the event */
6828         shiftKey : false,
6829         /** True if the control key was down during the event */
6830         ctrlKey : false,
6831         /** True if the alt key was down during the event */
6832         altKey : false,
6833
6834         /** Key constant 
6835         * @type Number */
6836         BACKSPACE : 8,
6837         /** Key constant 
6838         * @type Number */
6839         TAB : 9,
6840         /** Key constant 
6841         * @type Number */
6842         RETURN : 13,
6843         /** Key constant 
6844         * @type Number */
6845         ENTER : 13,
6846         /** Key constant 
6847         * @type Number */
6848         SHIFT : 16,
6849         /** Key constant 
6850         * @type Number */
6851         CONTROL : 17,
6852         /** Key constant 
6853         * @type Number */
6854         ESC : 27,
6855         /** Key constant 
6856         * @type Number */
6857         SPACE : 32,
6858         /** Key constant 
6859         * @type Number */
6860         PAGEUP : 33,
6861         /** Key constant 
6862         * @type Number */
6863         PAGEDOWN : 34,
6864         /** Key constant 
6865         * @type Number */
6866         END : 35,
6867         /** Key constant 
6868         * @type Number */
6869         HOME : 36,
6870         /** Key constant 
6871         * @type Number */
6872         LEFT : 37,
6873         /** Key constant 
6874         * @type Number */
6875         UP : 38,
6876         /** Key constant 
6877         * @type Number */
6878         RIGHT : 39,
6879         /** Key constant 
6880         * @type Number */
6881         DOWN : 40,
6882         /** Key constant 
6883         * @type Number */
6884         DELETE : 46,
6885         /** Key constant 
6886         * @type Number */
6887         F5 : 116,
6888
6889            /** @private */
6890         setEvent : function(e){
6891             if(e == this || (e && e.browserEvent)){ // already wrapped
6892                 return e;
6893             }
6894             this.browserEvent = e;
6895             if(e){
6896                 // normalize buttons
6897                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6898                 if(e.type == 'click' && this.button == -1){
6899                     this.button = 0;
6900                 }
6901                 this.type = e.type;
6902                 this.shiftKey = e.shiftKey;
6903                 // mac metaKey behaves like ctrlKey
6904                 this.ctrlKey = e.ctrlKey || e.metaKey;
6905                 this.altKey = e.altKey;
6906                 // in getKey these will be normalized for the mac
6907                 this.keyCode = e.keyCode;
6908                 // keyup warnings on firefox.
6909                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6910                 // cache the target for the delayed and or buffered events
6911                 this.target = E.getTarget(e);
6912                 // same for XY
6913                 this.xy = E.getXY(e);
6914             }else{
6915                 this.button = -1;
6916                 this.shiftKey = false;
6917                 this.ctrlKey = false;
6918                 this.altKey = false;
6919                 this.keyCode = 0;
6920                 this.charCode =0;
6921                 this.target = null;
6922                 this.xy = [0, 0];
6923             }
6924             return this;
6925         },
6926
6927         /**
6928          * Stop the event (preventDefault and stopPropagation)
6929          */
6930         stopEvent : function(){
6931             if(this.browserEvent){
6932                 if(this.browserEvent.type == 'mousedown'){
6933                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6934                 }
6935                 E.stopEvent(this.browserEvent);
6936             }
6937         },
6938
6939         /**
6940          * Prevents the browsers default handling of the event.
6941          */
6942         preventDefault : function(){
6943             if(this.browserEvent){
6944                 E.preventDefault(this.browserEvent);
6945             }
6946         },
6947
6948         /** @private */
6949         isNavKeyPress : function(){
6950             var k = this.keyCode;
6951             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6952             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6953         },
6954
6955         isSpecialKey : function(){
6956             var k = this.keyCode;
6957             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6958             (k == 16) || (k == 17) ||
6959             (k >= 18 && k <= 20) ||
6960             (k >= 33 && k <= 35) ||
6961             (k >= 36 && k <= 39) ||
6962             (k >= 44 && k <= 45);
6963         },
6964         /**
6965          * Cancels bubbling of the event.
6966          */
6967         stopPropagation : function(){
6968             if(this.browserEvent){
6969                 if(this.type == 'mousedown'){
6970                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6971                 }
6972                 E.stopPropagation(this.browserEvent);
6973             }
6974         },
6975
6976         /**
6977          * Gets the key code for the event.
6978          * @return {Number}
6979          */
6980         getCharCode : function(){
6981             return this.charCode || this.keyCode;
6982         },
6983
6984         /**
6985          * Returns a normalized keyCode for the event.
6986          * @return {Number} The key code
6987          */
6988         getKey : function(){
6989             var k = this.keyCode || this.charCode;
6990             return Roo.isSafari ? (safariKeys[k] || k) : k;
6991         },
6992
6993         /**
6994          * Gets the x coordinate of the event.
6995          * @return {Number}
6996          */
6997         getPageX : function(){
6998             return this.xy[0];
6999         },
7000
7001         /**
7002          * Gets the y coordinate of the event.
7003          * @return {Number}
7004          */
7005         getPageY : function(){
7006             return this.xy[1];
7007         },
7008
7009         /**
7010          * Gets the time of the event.
7011          * @return {Number}
7012          */
7013         getTime : function(){
7014             if(this.browserEvent){
7015                 return E.getTime(this.browserEvent);
7016             }
7017             return null;
7018         },
7019
7020         /**
7021          * Gets the page coordinates of the event.
7022          * @return {Array} The xy values like [x, y]
7023          */
7024         getXY : function(){
7025             return this.xy;
7026         },
7027
7028         /**
7029          * Gets the target for the event.
7030          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7032                 search as a number or element (defaults to 10 || document.body)
7033          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7034          * @return {HTMLelement}
7035          */
7036         getTarget : function(selector, maxDepth, returnEl){
7037             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7038         },
7039         /**
7040          * Gets the related target.
7041          * @return {HTMLElement}
7042          */
7043         getRelatedTarget : function(){
7044             if(this.browserEvent){
7045                 return E.getRelatedTarget(this.browserEvent);
7046             }
7047             return null;
7048         },
7049
7050         /**
7051          * Normalizes mouse wheel delta across browsers
7052          * @return {Number} The delta
7053          */
7054         getWheelDelta : function(){
7055             var e = this.browserEvent;
7056             var delta = 0;
7057             if(e.wheelDelta){ /* IE/Opera. */
7058                 delta = e.wheelDelta/120;
7059             }else if(e.detail){ /* Mozilla case. */
7060                 delta = -e.detail/3;
7061             }
7062             return delta;
7063         },
7064
7065         /**
7066          * Returns true if the control, meta, shift or alt key was pressed during this event.
7067          * @return {Boolean}
7068          */
7069         hasModifier : function(){
7070             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7071         },
7072
7073         /**
7074          * Returns true if the target of this event equals el or is a child of el
7075          * @param {String/HTMLElement/Element} el
7076          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7077          * @return {Boolean}
7078          */
7079         within : function(el, related){
7080             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7081             return t && Roo.fly(el).contains(t);
7082         },
7083
7084         getPoint : function(){
7085             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7086         }
7087     };
7088
7089     return new Roo.EventObjectImpl();
7090 }();
7091             
7092     /*
7093  * Based on:
7094  * Ext JS Library 1.1.1
7095  * Copyright(c) 2006-2007, Ext JS, LLC.
7096  *
7097  * Originally Released Under LGPL - original licence link has changed is not relivant.
7098  *
7099  * Fork - LGPL
7100  * <script type="text/javascript">
7101  */
7102
7103  
7104 // was in Composite Element!??!?!
7105  
7106 (function(){
7107     var D = Roo.lib.Dom;
7108     var E = Roo.lib.Event;
7109     var A = Roo.lib.Anim;
7110
7111     // local style camelizing for speed
7112     var propCache = {};
7113     var camelRe = /(-[a-z])/gi;
7114     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7115     var view = document.defaultView;
7116
7117 /**
7118  * @class Roo.Element
7119  * Represents an Element in the DOM.<br><br>
7120  * Usage:<br>
7121 <pre><code>
7122 var el = Roo.get("my-div");
7123
7124 // or with getEl
7125 var el = getEl("my-div");
7126
7127 // or with a DOM element
7128 var el = Roo.get(myDivElement);
7129 </code></pre>
7130  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7131  * each call instead of constructing a new one.<br><br>
7132  * <b>Animations</b><br />
7133  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7134  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7135 <pre>
7136 Option    Default   Description
7137 --------- --------  ---------------------------------------------
7138 duration  .35       The duration of the animation in seconds
7139 easing    easeOut   The YUI easing method
7140 callback  none      A function to execute when the anim completes
7141 scope     this      The scope (this) of the callback function
7142 </pre>
7143 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7144 * manipulate the animation. Here's an example:
7145 <pre><code>
7146 var el = Roo.get("my-div");
7147
7148 // no animation
7149 el.setWidth(100);
7150
7151 // default animation
7152 el.setWidth(100, true);
7153
7154 // animation with some options set
7155 el.setWidth(100, {
7156     duration: 1,
7157     callback: this.foo,
7158     scope: this
7159 });
7160
7161 // using the "anim" property to get the Anim object
7162 var opt = {
7163     duration: 1,
7164     callback: this.foo,
7165     scope: this
7166 };
7167 el.setWidth(100, opt);
7168 ...
7169 if(opt.anim.isAnimated()){
7170     opt.anim.stop();
7171 }
7172 </code></pre>
7173 * <b> Composite (Collections of) Elements</b><br />
7174  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7175  * @constructor Create a new Element directly.
7176  * @param {String/HTMLElement} element
7177  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7178  */
7179     Roo.Element = function(element, forceNew)
7180     {
7181         var dom = typeof element == "string" ?
7182                 document.getElementById(element) : element;
7183         
7184         this.listeners = {};
7185         
7186         if(!dom){ // invalid id/element
7187             return null;
7188         }
7189         var id = dom.id;
7190         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7191             return Roo.Element.cache[id];
7192         }
7193
7194         /**
7195          * The DOM element
7196          * @type HTMLElement
7197          */
7198         this.dom = dom;
7199
7200         /**
7201          * The DOM element ID
7202          * @type String
7203          */
7204         this.id = id || Roo.id(dom);
7205         
7206         return this; // assumed for cctor?
7207     };
7208
7209     var El = Roo.Element;
7210
7211     El.prototype = {
7212         /**
7213          * The element's default display mode  (defaults to "") 
7214          * @type String
7215          */
7216         originalDisplay : "",
7217
7218         
7219         // note this is overridden in BS version..
7220         visibilityMode : 1, 
7221         /**
7222          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7223          * @type String
7224          */
7225         defaultUnit : "px",
7226         
7227         /**
7228          * Sets the element's visibility mode. When setVisible() is called it
7229          * will use this to determine whether to set the visibility or the display property.
7230          * @param visMode Element.VISIBILITY or Element.DISPLAY
7231          * @return {Roo.Element} this
7232          */
7233         setVisibilityMode : function(visMode){
7234             this.visibilityMode = visMode;
7235             return this;
7236         },
7237         /**
7238          * Convenience method for setVisibilityMode(Element.DISPLAY)
7239          * @param {String} display (optional) What to set display to when visible
7240          * @return {Roo.Element} this
7241          */
7242         enableDisplayMode : function(display){
7243             this.setVisibilityMode(El.DISPLAY);
7244             if(typeof display != "undefined") { this.originalDisplay = display; }
7245             return this;
7246         },
7247
7248         /**
7249          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7250          * @param {String} selector The simple selector to test
7251          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7252                 search as a number or element (defaults to 10 || document.body)
7253          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7254          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7255          */
7256         findParent : function(simpleSelector, maxDepth, returnEl){
7257             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7258             maxDepth = maxDepth || 50;
7259             if(typeof maxDepth != "number"){
7260                 stopEl = Roo.getDom(maxDepth);
7261                 maxDepth = 10;
7262             }
7263             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7264                 if(dq.is(p, simpleSelector)){
7265                     return returnEl ? Roo.get(p) : p;
7266                 }
7267                 depth++;
7268                 p = p.parentNode;
7269             }
7270             return null;
7271         },
7272
7273
7274         /**
7275          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7276          * @param {String} selector The simple selector to test
7277          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7278                 search as a number or element (defaults to 10 || document.body)
7279          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7280          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7281          */
7282         findParentNode : function(simpleSelector, maxDepth, returnEl){
7283             var p = Roo.fly(this.dom.parentNode, '_internal');
7284             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7285         },
7286         
7287         /**
7288          * Looks at  the scrollable parent element
7289          */
7290         findScrollableParent : function()
7291         {
7292             var overflowRegex = /(auto|scroll)/;
7293             
7294             if(this.getStyle('position') === 'fixed'){
7295                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7296             }
7297             
7298             var excludeStaticParent = this.getStyle('position') === "absolute";
7299             
7300             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7301                 
7302                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7303                     continue;
7304                 }
7305                 
7306                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7307                     return parent;
7308                 }
7309                 
7310                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7311                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7312                 }
7313             }
7314             
7315             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7316         },
7317
7318         /**
7319          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7320          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7321          * @param {String} selector The simple selector to test
7322          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7323                 search as a number or element (defaults to 10 || document.body)
7324          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7325          */
7326         up : function(simpleSelector, maxDepth){
7327             return this.findParentNode(simpleSelector, maxDepth, true);
7328         },
7329
7330
7331
7332         /**
7333          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7334          * @param {String} selector The simple selector to test
7335          * @return {Boolean} True if this element matches the selector, else false
7336          */
7337         is : function(simpleSelector){
7338             return Roo.DomQuery.is(this.dom, simpleSelector);
7339         },
7340
7341         /**
7342          * Perform animation on this element.
7343          * @param {Object} args The YUI animation control args
7344          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7345          * @param {Function} onComplete (optional) Function to call when animation completes
7346          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7347          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7348          * @return {Roo.Element} this
7349          */
7350         animate : function(args, duration, onComplete, easing, animType){
7351             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7352             return this;
7353         },
7354
7355         /*
7356          * @private Internal animation call
7357          */
7358         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7359             animType = animType || 'run';
7360             opt = opt || {};
7361             var anim = Roo.lib.Anim[animType](
7362                 this.dom, args,
7363                 (opt.duration || defaultDur) || .35,
7364                 (opt.easing || defaultEase) || 'easeOut',
7365                 function(){
7366                     Roo.callback(cb, this);
7367                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7368                 },
7369                 this
7370             );
7371             opt.anim = anim;
7372             return anim;
7373         },
7374
7375         // private legacy anim prep
7376         preanim : function(a, i){
7377             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7378         },
7379
7380         /**
7381          * Removes worthless text nodes
7382          * @param {Boolean} forceReclean (optional) By default the element
7383          * keeps track if it has been cleaned already so
7384          * you can call this over and over. However, if you update the element and
7385          * need to force a reclean, you can pass true.
7386          */
7387         clean : function(forceReclean){
7388             if(this.isCleaned && forceReclean !== true){
7389                 return this;
7390             }
7391             var ns = /\S/;
7392             var d = this.dom, n = d.firstChild, ni = -1;
7393             while(n){
7394                 var nx = n.nextSibling;
7395                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7396                     d.removeChild(n);
7397                 }else{
7398                     n.nodeIndex = ++ni;
7399                 }
7400                 n = nx;
7401             }
7402             this.isCleaned = true;
7403             return this;
7404         },
7405
7406         // private
7407         calcOffsetsTo : function(el){
7408             el = Roo.get(el);
7409             var d = el.dom;
7410             var restorePos = false;
7411             if(el.getStyle('position') == 'static'){
7412                 el.position('relative');
7413                 restorePos = true;
7414             }
7415             var x = 0, y =0;
7416             var op = this.dom;
7417             while(op && op != d && op.tagName != 'HTML'){
7418                 x+= op.offsetLeft;
7419                 y+= op.offsetTop;
7420                 op = op.offsetParent;
7421             }
7422             if(restorePos){
7423                 el.position('static');
7424             }
7425             return [x, y];
7426         },
7427
7428         /**
7429          * Scrolls this element into view within the passed container.
7430          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7431          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7432          * @return {Roo.Element} this
7433          */
7434         scrollIntoView : function(container, hscroll){
7435             var c = Roo.getDom(container) || document.body;
7436             var el = this.dom;
7437
7438             var o = this.calcOffsetsTo(c),
7439                 l = o[0],
7440                 t = o[1],
7441                 b = t+el.offsetHeight,
7442                 r = l+el.offsetWidth;
7443
7444             var ch = c.clientHeight;
7445             var ct = parseInt(c.scrollTop, 10);
7446             var cl = parseInt(c.scrollLeft, 10);
7447             var cb = ct + ch;
7448             var cr = cl + c.clientWidth;
7449
7450             if(t < ct){
7451                 c.scrollTop = t;
7452             }else if(b > cb){
7453                 c.scrollTop = b-ch;
7454             }
7455
7456             if(hscroll !== false){
7457                 if(l < cl){
7458                     c.scrollLeft = l;
7459                 }else if(r > cr){
7460                     c.scrollLeft = r-c.clientWidth;
7461                 }
7462             }
7463             return this;
7464         },
7465
7466         // private
7467         scrollChildIntoView : function(child, hscroll){
7468             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7469         },
7470
7471         /**
7472          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7473          * the new height may not be available immediately.
7474          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7475          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7476          * @param {Function} onComplete (optional) Function to call when animation completes
7477          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7478          * @return {Roo.Element} this
7479          */
7480         autoHeight : function(animate, duration, onComplete, easing){
7481             var oldHeight = this.getHeight();
7482             this.clip();
7483             this.setHeight(1); // force clipping
7484             setTimeout(function(){
7485                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7486                 if(!animate){
7487                     this.setHeight(height);
7488                     this.unclip();
7489                     if(typeof onComplete == "function"){
7490                         onComplete();
7491                     }
7492                 }else{
7493                     this.setHeight(oldHeight); // restore original height
7494                     this.setHeight(height, animate, duration, function(){
7495                         this.unclip();
7496                         if(typeof onComplete == "function") { onComplete(); }
7497                     }.createDelegate(this), easing);
7498                 }
7499             }.createDelegate(this), 0);
7500             return this;
7501         },
7502
7503         /**
7504          * Returns true if this element is an ancestor of the passed element
7505          * @param {HTMLElement/String} el The element to check
7506          * @return {Boolean} True if this element is an ancestor of el, else false
7507          */
7508         contains : function(el){
7509             if(!el){return false;}
7510             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7511         },
7512
7513         /**
7514          * Checks whether the element is currently visible using both visibility and display properties.
7515          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7516          * @return {Boolean} True if the element is currently visible, else false
7517          */
7518         isVisible : function(deep) {
7519             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7520             if(deep !== true || !vis){
7521                 return vis;
7522             }
7523             var p = this.dom.parentNode;
7524             while(p && p.tagName.toLowerCase() != "body"){
7525                 if(!Roo.fly(p, '_isVisible').isVisible()){
7526                     return false;
7527                 }
7528                 p = p.parentNode;
7529             }
7530             return true;
7531         },
7532
7533         /**
7534          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7535          * @param {String} selector The CSS selector
7536          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7537          * @return {CompositeElement/CompositeElementLite} The composite element
7538          */
7539         select : function(selector, unique){
7540             return El.select(selector, unique, this.dom);
7541         },
7542
7543         /**
7544          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7545          * @param {String} selector The CSS selector
7546          * @return {Array} An array of the matched nodes
7547          */
7548         query : function(selector, unique){
7549             return Roo.DomQuery.select(selector, this.dom);
7550         },
7551
7552         /**
7553          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7554          * @param {String} selector The CSS selector
7555          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7556          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7557          */
7558         child : function(selector, returnDom){
7559             var n = Roo.DomQuery.selectNode(selector, this.dom);
7560             return returnDom ? n : Roo.get(n);
7561         },
7562
7563         /**
7564          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7565          * @param {String} selector The CSS selector
7566          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7567          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7568          */
7569         down : function(selector, returnDom){
7570             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7571             return returnDom ? n : Roo.get(n);
7572         },
7573
7574         /**
7575          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7576          * @param {String} group The group the DD object is member of
7577          * @param {Object} config The DD config object
7578          * @param {Object} overrides An object containing methods to override/implement on the DD object
7579          * @return {Roo.dd.DD} The DD object
7580          */
7581         initDD : function(group, config, overrides){
7582             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7583             return Roo.apply(dd, overrides);
7584         },
7585
7586         /**
7587          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7588          * @param {String} group The group the DDProxy object is member of
7589          * @param {Object} config The DDProxy config object
7590          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7591          * @return {Roo.dd.DDProxy} The DDProxy object
7592          */
7593         initDDProxy : function(group, config, overrides){
7594             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7595             return Roo.apply(dd, overrides);
7596         },
7597
7598         /**
7599          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7600          * @param {String} group The group the DDTarget object is member of
7601          * @param {Object} config The DDTarget config object
7602          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7603          * @return {Roo.dd.DDTarget} The DDTarget object
7604          */
7605         initDDTarget : function(group, config, overrides){
7606             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7607             return Roo.apply(dd, overrides);
7608         },
7609
7610         /**
7611          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7612          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7613          * @param {Boolean} visible Whether the element is visible
7614          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7615          * @return {Roo.Element} this
7616          */
7617          setVisible : function(visible, animate){
7618             if(!animate || !A){
7619                 if(this.visibilityMode == El.DISPLAY){
7620                     this.setDisplayed(visible);
7621                 }else{
7622                     this.fixDisplay();
7623                     this.dom.style.visibility = visible ? "visible" : "hidden";
7624                 }
7625             }else{
7626                 // closure for composites
7627                 var dom = this.dom;
7628                 var visMode = this.visibilityMode;
7629                 if(visible){
7630                     this.setOpacity(.01);
7631                     this.setVisible(true);
7632                 }
7633                 this.anim({opacity: { to: (visible?1:0) }},
7634                       this.preanim(arguments, 1),
7635                       null, .35, 'easeIn', function(){
7636                          if(!visible){
7637                              if(visMode == El.DISPLAY){
7638                                  dom.style.display = "none";
7639                              }else{
7640                                  dom.style.visibility = "hidden";
7641                              }
7642                              Roo.get(dom).setOpacity(1);
7643                          }
7644                      });
7645             }
7646             return this;
7647         },
7648
7649         /**
7650          * Returns true if display is not "none"
7651          * @return {Boolean}
7652          */
7653         isDisplayed : function() {
7654             return this.getStyle("display") != "none";
7655         },
7656
7657         /**
7658          * Toggles the element's visibility or display, depending on visibility mode.
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         toggle : function(animate){
7663             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7669          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7670          * @return {Roo.Element} this
7671          */
7672         setDisplayed : function(value) {
7673             if(typeof value == "boolean"){
7674                value = value ? this.originalDisplay : "none";
7675             }
7676             this.setStyle("display", value);
7677             return this;
7678         },
7679
7680         /**
7681          * Tries to focus the element. Any exceptions are caught and ignored.
7682          * @return {Roo.Element} this
7683          */
7684         focus : function() {
7685             try{
7686                 this.dom.focus();
7687             }catch(e){}
7688             return this;
7689         },
7690
7691         /**
7692          * Tries to blur the element. Any exceptions are caught and ignored.
7693          * @return {Roo.Element} this
7694          */
7695         blur : function() {
7696             try{
7697                 this.dom.blur();
7698             }catch(e){}
7699             return this;
7700         },
7701
7702         /**
7703          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7704          * @param {String/Array} className The CSS class to add, or an array of classes
7705          * @return {Roo.Element} this
7706          */
7707         addClass : function(className){
7708             if(className instanceof Array){
7709                 for(var i = 0, len = className.length; i < len; i++) {
7710                     this.addClass(className[i]);
7711                 }
7712             }else{
7713                 if(className && !this.hasClass(className)){
7714                     if (this.dom instanceof SVGElement) {
7715                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7716                     } else {
7717                         this.dom.className = this.dom.className + " " + className;
7718                     }
7719                 }
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7726          * @param {String/Array} className The CSS class to add, or an array of classes
7727          * @return {Roo.Element} this
7728          */
7729         radioClass : function(className){
7730             var siblings = this.dom.parentNode.childNodes;
7731             for(var i = 0; i < siblings.length; i++) {
7732                 var s = siblings[i];
7733                 if(s.nodeType == 1){
7734                     Roo.get(s).removeClass(className);
7735                 }
7736             }
7737             this.addClass(className);
7738             return this;
7739         },
7740
7741         /**
7742          * Removes one or more CSS classes from the element.
7743          * @param {String/Array} className The CSS class to remove, or an array of classes
7744          * @return {Roo.Element} this
7745          */
7746         removeClass : function(className){
7747             
7748             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7749             if(!className || !cn){
7750                 return this;
7751             }
7752             if(className instanceof Array){
7753                 for(var i = 0, len = className.length; i < len; i++) {
7754                     this.removeClass(className[i]);
7755                 }
7756             }else{
7757                 if(this.hasClass(className)){
7758                     var re = this.classReCache[className];
7759                     if (!re) {
7760                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7761                        this.classReCache[className] = re;
7762                     }
7763                     if (this.dom instanceof SVGElement) {
7764                         this.dom.className.baseVal = cn.replace(re, " ");
7765                     } else {
7766                         this.dom.className = cn.replace(re, " ");
7767                     }
7768                 }
7769             }
7770             return this;
7771         },
7772
7773         // private
7774         classReCache: {},
7775
7776         /**
7777          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7778          * @param {String} className The CSS class to toggle
7779          * @return {Roo.Element} this
7780          */
7781         toggleClass : function(className){
7782             if(this.hasClass(className)){
7783                 this.removeClass(className);
7784             }else{
7785                 this.addClass(className);
7786             }
7787             return this;
7788         },
7789
7790         /**
7791          * Checks if the specified CSS class exists on this element's DOM node.
7792          * @param {String} className The CSS class to check for
7793          * @return {Boolean} True if the class exists, else false
7794          */
7795         hasClass : function(className){
7796             if (this.dom instanceof SVGElement) {
7797                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7798             } 
7799             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7800         },
7801
7802         /**
7803          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7804          * @param {String} oldClassName The CSS class to replace
7805          * @param {String} newClassName The replacement CSS class
7806          * @return {Roo.Element} this
7807          */
7808         replaceClass : function(oldClassName, newClassName){
7809             this.removeClass(oldClassName);
7810             this.addClass(newClassName);
7811             return this;
7812         },
7813
7814         /**
7815          * Returns an object with properties matching the styles requested.
7816          * For example, el.getStyles('color', 'font-size', 'width') might return
7817          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7818          * @param {String} style1 A style name
7819          * @param {String} style2 A style name
7820          * @param {String} etc.
7821          * @return {Object} The style object
7822          */
7823         getStyles : function(){
7824             var a = arguments, len = a.length, r = {};
7825             for(var i = 0; i < len; i++){
7826                 r[a[i]] = this.getStyle(a[i]);
7827             }
7828             return r;
7829         },
7830
7831         /**
7832          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7833          * @param {String} property The style property whose value is returned.
7834          * @return {String} The current value of the style property for this element.
7835          */
7836         getStyle : function(){
7837             return view && view.getComputedStyle ?
7838                 function(prop){
7839                     var el = this.dom, v, cs, camel;
7840                     if(prop == 'float'){
7841                         prop = "cssFloat";
7842                     }
7843                     if(el.style && (v = el.style[prop])){
7844                         return v;
7845                     }
7846                     if(cs = view.getComputedStyle(el, "")){
7847                         if(!(camel = propCache[prop])){
7848                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7849                         }
7850                         return cs[camel];
7851                     }
7852                     return null;
7853                 } :
7854                 function(prop){
7855                     var el = this.dom, v, cs, camel;
7856                     if(prop == 'opacity'){
7857                         if(typeof el.style.filter == 'string'){
7858                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7859                             if(m){
7860                                 var fv = parseFloat(m[1]);
7861                                 if(!isNaN(fv)){
7862                                     return fv ? fv / 100 : 0;
7863                                 }
7864                             }
7865                         }
7866                         return 1;
7867                     }else if(prop == 'float'){
7868                         prop = "styleFloat";
7869                     }
7870                     if(!(camel = propCache[prop])){
7871                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7872                     }
7873                     if(v = el.style[camel]){
7874                         return v;
7875                     }
7876                     if(cs = el.currentStyle){
7877                         return cs[camel];
7878                     }
7879                     return null;
7880                 };
7881         }(),
7882
7883         /**
7884          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7885          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7886          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7887          * @return {Roo.Element} this
7888          */
7889         setStyle : function(prop, value){
7890             if(typeof prop == "string"){
7891                 
7892                 if (prop == 'float') {
7893                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7894                     return this;
7895                 }
7896                 
7897                 var camel;
7898                 if(!(camel = propCache[prop])){
7899                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7900                 }
7901                 
7902                 if(camel == 'opacity') {
7903                     this.setOpacity(value);
7904                 }else{
7905                     this.dom.style[camel] = value;
7906                 }
7907             }else{
7908                 for(var style in prop){
7909                     if(typeof prop[style] != "function"){
7910                        this.setStyle(style, prop[style]);
7911                     }
7912                 }
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * More flexible version of {@link #setStyle} for setting style properties.
7919          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7920          * a function which returns such a specification.
7921          * @return {Roo.Element} this
7922          */
7923         applyStyles : function(style){
7924             Roo.DomHelper.applyStyles(this.dom, style);
7925             return this;
7926         },
7927
7928         /**
7929           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930           * @return {Number} The X position of the element
7931           */
7932         getX : function(){
7933             return D.getX(this.dom);
7934         },
7935
7936         /**
7937           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7938           * @return {Number} The Y position of the element
7939           */
7940         getY : function(){
7941             return D.getY(this.dom);
7942         },
7943
7944         /**
7945           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946           * @return {Array} The XY position of the element
7947           */
7948         getXY : function(){
7949             return D.getXY(this.dom);
7950         },
7951
7952         /**
7953          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7954          * @param {Number} The X position of the element
7955          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7956          * @return {Roo.Element} this
7957          */
7958         setX : function(x, animate){
7959             if(!animate || !A){
7960                 D.setX(this.dom, x);
7961             }else{
7962                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7963             }
7964             return this;
7965         },
7966
7967         /**
7968          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7969          * @param {Number} The Y position of the element
7970          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setY : function(y, animate){
7974             if(!animate || !A){
7975                 D.setY(this.dom, y);
7976             }else{
7977                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7978             }
7979             return this;
7980         },
7981
7982         /**
7983          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7984          * @param {String} left The left CSS property value
7985          * @return {Roo.Element} this
7986          */
7987         setLeft : function(left){
7988             this.setStyle("left", this.addUnits(left));
7989             return this;
7990         },
7991
7992         /**
7993          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7994          * @param {String} top The top CSS property value
7995          * @return {Roo.Element} this
7996          */
7997         setTop : function(top){
7998             this.setStyle("top", this.addUnits(top));
7999             return this;
8000         },
8001
8002         /**
8003          * Sets the element's CSS right style.
8004          * @param {String} right The right CSS property value
8005          * @return {Roo.Element} this
8006          */
8007         setRight : function(right){
8008             this.setStyle("right", this.addUnits(right));
8009             return this;
8010         },
8011
8012         /**
8013          * Sets the element's CSS bottom style.
8014          * @param {String} bottom The bottom CSS property value
8015          * @return {Roo.Element} this
8016          */
8017         setBottom : function(bottom){
8018             this.setStyle("bottom", this.addUnits(bottom));
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8024          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8025          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8026          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setXY : function(pos, animate){
8030             if(!animate || !A){
8031                 D.setXY(this.dom, pos);
8032             }else{
8033                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8040          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8041          * @param {Number} x X value for new position (coordinates are page-based)
8042          * @param {Number} y Y value for new position (coordinates are page-based)
8043          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8044          * @return {Roo.Element} this
8045          */
8046         setLocation : function(x, y, animate){
8047             this.setXY([x, y], this.preanim(arguments, 2));
8048             return this;
8049         },
8050
8051         /**
8052          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8053          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8054          * @param {Number} x X value for new position (coordinates are page-based)
8055          * @param {Number} y Y value for new position (coordinates are page-based)
8056          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059         moveTo : function(x, y, animate){
8060             this.setXY([x, y], this.preanim(arguments, 2));
8061             return this;
8062         },
8063
8064         /**
8065          * Returns the region of the given element.
8066          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8067          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8068          */
8069         getRegion : function(){
8070             return D.getRegion(this.dom);
8071         },
8072
8073         /**
8074          * Returns the offset height of the element
8075          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8076          * @return {Number} The element's height
8077          */
8078         getHeight : function(contentHeight){
8079             var h = this.dom.offsetHeight || 0;
8080             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8081         },
8082
8083         /**
8084          * Returns the offset width of the element
8085          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8086          * @return {Number} The element's width
8087          */
8088         getWidth : function(contentWidth){
8089             var w = this.dom.offsetWidth || 0;
8090             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8091         },
8092
8093         /**
8094          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8095          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8096          * if a height has not been set using CSS.
8097          * @return {Number}
8098          */
8099         getComputedHeight : function(){
8100             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8101             if(!h){
8102                 h = parseInt(this.getStyle('height'), 10) || 0;
8103                 if(!this.isBorderBox()){
8104                     h += this.getFrameWidth('tb');
8105                 }
8106             }
8107             return h;
8108         },
8109
8110         /**
8111          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8112          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8113          * if a width has not been set using CSS.
8114          * @return {Number}
8115          */
8116         getComputedWidth : function(){
8117             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8118             if(!w){
8119                 w = parseInt(this.getStyle('width'), 10) || 0;
8120                 if(!this.isBorderBox()){
8121                     w += this.getFrameWidth('lr');
8122                 }
8123             }
8124             return w;
8125         },
8126
8127         /**
8128          * Returns the size of the element.
8129          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8130          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8131          */
8132         getSize : function(contentSize){
8133             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8134         },
8135
8136         /**
8137          * Returns the width and height of the viewport.
8138          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8139          */
8140         getViewSize : function(){
8141             var d = this.dom, doc = document, aw = 0, ah = 0;
8142             if(d == doc || d == doc.body){
8143                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8144             }else{
8145                 return {
8146                     width : d.clientWidth,
8147                     height: d.clientHeight
8148                 };
8149             }
8150         },
8151
8152         /**
8153          * Returns the value of the "value" attribute
8154          * @param {Boolean} asNumber true to parse the value as a number
8155          * @return {String/Number}
8156          */
8157         getValue : function(asNumber){
8158             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8159         },
8160
8161         // private
8162         adjustWidth : function(width){
8163             if(typeof width == "number"){
8164                 if(this.autoBoxAdjust && !this.isBorderBox()){
8165                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8166                 }
8167                 if(width < 0){
8168                     width = 0;
8169                 }
8170             }
8171             return width;
8172         },
8173
8174         // private
8175         adjustHeight : function(height){
8176             if(typeof height == "number"){
8177                if(this.autoBoxAdjust && !this.isBorderBox()){
8178                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8179                }
8180                if(height < 0){
8181                    height = 0;
8182                }
8183             }
8184             return height;
8185         },
8186
8187         /**
8188          * Set the width of the element
8189          * @param {Number} width The new width
8190          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8191          * @return {Roo.Element} this
8192          */
8193         setWidth : function(width, animate){
8194             width = this.adjustWidth(width);
8195             if(!animate || !A){
8196                 this.dom.style.width = this.addUnits(width);
8197             }else{
8198                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8199             }
8200             return this;
8201         },
8202
8203         /**
8204          * Set the height of the element
8205          * @param {Number} height The new height
8206          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8207          * @return {Roo.Element} this
8208          */
8209          setHeight : function(height, animate){
8210             height = this.adjustHeight(height);
8211             if(!animate || !A){
8212                 this.dom.style.height = this.addUnits(height);
8213             }else{
8214                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8215             }
8216             return this;
8217         },
8218
8219         /**
8220          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8221          * @param {Number} width The new width
8222          * @param {Number} height The new height
8223          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8224          * @return {Roo.Element} this
8225          */
8226          setSize : function(width, height, animate){
8227             if(typeof width == "object"){ // in case of object from getSize()
8228                 height = width.height; width = width.width;
8229             }
8230             width = this.adjustWidth(width); height = this.adjustHeight(height);
8231             if(!animate || !A){
8232                 this.dom.style.width = this.addUnits(width);
8233                 this.dom.style.height = this.addUnits(height);
8234             }else{
8235                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8242          * @param {Number} x X value for new position (coordinates are page-based)
8243          * @param {Number} y Y value for new position (coordinates are page-based)
8244          * @param {Number} width The new width
8245          * @param {Number} height The new height
8246          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8247          * @return {Roo.Element} this
8248          */
8249         setBounds : function(x, y, width, height, animate){
8250             if(!animate || !A){
8251                 this.setSize(width, height);
8252                 this.setLocation(x, y);
8253             }else{
8254                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8255                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8256                               this.preanim(arguments, 4), 'motion');
8257             }
8258             return this;
8259         },
8260
8261         /**
8262          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8263          * @param {Roo.lib.Region} region The region to fill
8264          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8265          * @return {Roo.Element} this
8266          */
8267         setRegion : function(region, animate){
8268             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8269             return this;
8270         },
8271
8272         /**
8273          * Appends an event handler
8274          *
8275          * @param {String}   eventName     The type of event to append
8276          * @param {Function} fn        The method the event invokes
8277          * @param {Object} scope       (optional) The scope (this object) of the fn
8278          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8279          */
8280         addListener : function(eventName, fn, scope, options)
8281         {
8282             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8283                 this.addListener('touchstart', this.onTapHandler, this);
8284             }
8285             
8286             // we need to handle a special case where dom element is a svg element.
8287             // in this case we do not actua
8288             if (!this.dom) {
8289                 return;
8290             }
8291             
8292             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8293                 if (typeof(this.listeners[eventName]) == 'undefined') {
8294                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8295                 }
8296                 this.listeners[eventName].addListener(fn, scope, options);
8297                 return;
8298             }
8299             
8300                 
8301             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8302             
8303             
8304         },
8305         tapedTwice : false,
8306         onTapHandler : function(event)
8307         {
8308             if(!this.tapedTwice) {
8309                 this.tapedTwice = true;
8310                 var s = this;
8311                 setTimeout( function() {
8312                     s.tapedTwice = false;
8313                 }, 300 );
8314                 return;
8315             }
8316             event.preventDefault();
8317             var revent = new MouseEvent('dblclick',  {
8318                 view: window,
8319                 bubbles: true,
8320                 cancelable: true
8321             });
8322              
8323             this.dom.dispatchEvent(revent);
8324             //action on double tap goes below
8325              
8326         }, 
8327  
8328         /**
8329          * Removes an event handler from this element
8330          * @param {String} eventName the type of event to remove
8331          * @param {Function} fn the method the event invokes
8332          * @param {Function} scope (needed for svg fake listeners)
8333          * @return {Roo.Element} this
8334          */
8335         removeListener : function(eventName, fn, scope){
8336             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8337             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8338                 return this;
8339             }
8340             this.listeners[eventName].removeListener(fn, scope);
8341             return this;
8342         },
8343
8344         /**
8345          * Removes all previous added listeners from this element
8346          * @return {Roo.Element} this
8347          */
8348         removeAllListeners : function(){
8349             E.purgeElement(this.dom);
8350             this.listeners = {};
8351             return this;
8352         },
8353
8354         relayEvent : function(eventName, observable){
8355             this.on(eventName, function(e){
8356                 observable.fireEvent(eventName, e);
8357             });
8358         },
8359
8360         
8361         /**
8362          * Set the opacity of the element
8363          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367          setOpacity : function(opacity, animate){
8368             if(!animate || !A){
8369                 var s = this.dom.style;
8370                 if(Roo.isIE){
8371                     s.zoom = 1;
8372                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8373                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8374                 }else{
8375                     s.opacity = opacity;
8376                 }
8377             }else{
8378                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8379             }
8380             return this;
8381         },
8382
8383         /**
8384          * Gets the left X coordinate
8385          * @param {Boolean} local True to get the local css position instead of page coordinate
8386          * @return {Number}
8387          */
8388         getLeft : function(local){
8389             if(!local){
8390                 return this.getX();
8391             }else{
8392                 return parseInt(this.getStyle("left"), 10) || 0;
8393             }
8394         },
8395
8396         /**
8397          * Gets the right X coordinate of the element (element X position + element width)
8398          * @param {Boolean} local True to get the local css position instead of page coordinate
8399          * @return {Number}
8400          */
8401         getRight : function(local){
8402             if(!local){
8403                 return this.getX() + this.getWidth();
8404             }else{
8405                 return (this.getLeft(true) + this.getWidth()) || 0;
8406             }
8407         },
8408
8409         /**
8410          * Gets the top Y coordinate
8411          * @param {Boolean} local True to get the local css position instead of page coordinate
8412          * @return {Number}
8413          */
8414         getTop : function(local) {
8415             if(!local){
8416                 return this.getY();
8417             }else{
8418                 return parseInt(this.getStyle("top"), 10) || 0;
8419             }
8420         },
8421
8422         /**
8423          * Gets the bottom Y coordinate of the element (element Y position + element height)
8424          * @param {Boolean} local True to get the local css position instead of page coordinate
8425          * @return {Number}
8426          */
8427         getBottom : function(local){
8428             if(!local){
8429                 return this.getY() + this.getHeight();
8430             }else{
8431                 return (this.getTop(true) + this.getHeight()) || 0;
8432             }
8433         },
8434
8435         /**
8436         * Initializes positioning on this element. If a desired position is not passed, it will make the
8437         * the element positioned relative IF it is not already positioned.
8438         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8439         * @param {Number} zIndex (optional) The zIndex to apply
8440         * @param {Number} x (optional) Set the page X position
8441         * @param {Number} y (optional) Set the page Y position
8442         */
8443         position : function(pos, zIndex, x, y){
8444             if(!pos){
8445                if(this.getStyle('position') == 'static'){
8446                    this.setStyle('position', 'relative');
8447                }
8448             }else{
8449                 this.setStyle("position", pos);
8450             }
8451             if(zIndex){
8452                 this.setStyle("z-index", zIndex);
8453             }
8454             if(x !== undefined && y !== undefined){
8455                 this.setXY([x, y]);
8456             }else if(x !== undefined){
8457                 this.setX(x);
8458             }else if(y !== undefined){
8459                 this.setY(y);
8460             }
8461         },
8462
8463         /**
8464         * Clear positioning back to the default when the document was loaded
8465         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8466         * @return {Roo.Element} this
8467          */
8468         clearPositioning : function(value){
8469             value = value ||'';
8470             this.setStyle({
8471                 "left": value,
8472                 "right": value,
8473                 "top": value,
8474                 "bottom": value,
8475                 "z-index": "",
8476                 "position" : "static"
8477             });
8478             return this;
8479         },
8480
8481         /**
8482         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8483         * snapshot before performing an update and then restoring the element.
8484         * @return {Object}
8485         */
8486         getPositioning : function(){
8487             var l = this.getStyle("left");
8488             var t = this.getStyle("top");
8489             return {
8490                 "position" : this.getStyle("position"),
8491                 "left" : l,
8492                 "right" : l ? "" : this.getStyle("right"),
8493                 "top" : t,
8494                 "bottom" : t ? "" : this.getStyle("bottom"),
8495                 "z-index" : this.getStyle("z-index")
8496             };
8497         },
8498
8499         /**
8500          * Gets the width of the border(s) for the specified side(s)
8501          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8502          * passing lr would get the border (l)eft width + the border (r)ight width.
8503          * @return {Number} The width of the sides passed added together
8504          */
8505         getBorderWidth : function(side){
8506             return this.addStyles(side, El.borders);
8507         },
8508
8509         /**
8510          * Gets the width of the padding(s) for the specified side(s)
8511          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8512          * passing lr would get the padding (l)eft + the padding (r)ight.
8513          * @return {Number} The padding of the sides passed added together
8514          */
8515         getPadding : function(side){
8516             return this.addStyles(side, El.paddings);
8517         },
8518
8519         /**
8520         * Set positioning with an object returned by getPositioning().
8521         * @param {Object} posCfg
8522         * @return {Roo.Element} this
8523          */
8524         setPositioning : function(pc){
8525             this.applyStyles(pc);
8526             if(pc.right == "auto"){
8527                 this.dom.style.right = "";
8528             }
8529             if(pc.bottom == "auto"){
8530                 this.dom.style.bottom = "";
8531             }
8532             return this;
8533         },
8534
8535         // private
8536         fixDisplay : function(){
8537             if(this.getStyle("display") == "none"){
8538                 this.setStyle("visibility", "hidden");
8539                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8540                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8541                     this.setStyle("display", "block");
8542                 }
8543             }
8544         },
8545
8546         /**
8547          * Quick set left and top adding default units
8548          * @param {String} left The left CSS property value
8549          * @param {String} top The top CSS property value
8550          * @return {Roo.Element} this
8551          */
8552          setLeftTop : function(left, top){
8553             this.dom.style.left = this.addUnits(left);
8554             this.dom.style.top = this.addUnits(top);
8555             return this;
8556         },
8557
8558         /**
8559          * Move this element relative to its current position.
8560          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8561          * @param {Number} distance How far to move the element in pixels
8562          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8563          * @return {Roo.Element} this
8564          */
8565          move : function(direction, distance, animate){
8566             var xy = this.getXY();
8567             direction = direction.toLowerCase();
8568             switch(direction){
8569                 case "l":
8570                 case "left":
8571                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8572                     break;
8573                case "r":
8574                case "right":
8575                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8576                     break;
8577                case "t":
8578                case "top":
8579                case "up":
8580                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8581                     break;
8582                case "b":
8583                case "bottom":
8584                case "down":
8585                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8586                     break;
8587             }
8588             return this;
8589         },
8590
8591         /**
8592          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8593          * @return {Roo.Element} this
8594          */
8595         clip : function(){
8596             if(!this.isClipped){
8597                this.isClipped = true;
8598                this.originalClip = {
8599                    "o": this.getStyle("overflow"),
8600                    "x": this.getStyle("overflow-x"),
8601                    "y": this.getStyle("overflow-y")
8602                };
8603                this.setStyle("overflow", "hidden");
8604                this.setStyle("overflow-x", "hidden");
8605                this.setStyle("overflow-y", "hidden");
8606             }
8607             return this;
8608         },
8609
8610         /**
8611          *  Return clipping (overflow) to original clipping before clip() was called
8612          * @return {Roo.Element} this
8613          */
8614         unclip : function(){
8615             if(this.isClipped){
8616                 this.isClipped = false;
8617                 var o = this.originalClip;
8618                 if(o.o){this.setStyle("overflow", o.o);}
8619                 if(o.x){this.setStyle("overflow-x", o.x);}
8620                 if(o.y){this.setStyle("overflow-y", o.y);}
8621             }
8622             return this;
8623         },
8624
8625
8626         /**
8627          * Gets the x,y coordinates specified by the anchor position on the element.
8628          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8629          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8630          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8631          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8632          * @return {Array} [x, y] An array containing the element's x and y coordinates
8633          */
8634         getAnchorXY : function(anchor, local, s){
8635             //Passing a different size is useful for pre-calculating anchors,
8636             //especially for anchored animations that change the el size.
8637
8638             var w, h, vp = false;
8639             if(!s){
8640                 var d = this.dom;
8641                 if(d == document.body || d == document){
8642                     vp = true;
8643                     w = D.getViewWidth(); h = D.getViewHeight();
8644                 }else{
8645                     w = this.getWidth(); h = this.getHeight();
8646                 }
8647             }else{
8648                 w = s.width;  h = s.height;
8649             }
8650             var x = 0, y = 0, r = Math.round;
8651             switch((anchor || "tl").toLowerCase()){
8652                 case "c":
8653                     x = r(w*.5);
8654                     y = r(h*.5);
8655                 break;
8656                 case "t":
8657                     x = r(w*.5);
8658                     y = 0;
8659                 break;
8660                 case "l":
8661                     x = 0;
8662                     y = r(h*.5);
8663                 break;
8664                 case "r":
8665                     x = w;
8666                     y = r(h*.5);
8667                 break;
8668                 case "b":
8669                     x = r(w*.5);
8670                     y = h;
8671                 break;
8672                 case "tl":
8673                     x = 0;
8674                     y = 0;
8675                 break;
8676                 case "bl":
8677                     x = 0;
8678                     y = h;
8679                 break;
8680                 case "br":
8681                     x = w;
8682                     y = h;
8683                 break;
8684                 case "tr":
8685                     x = w;
8686                     y = 0;
8687                 break;
8688             }
8689             if(local === true){
8690                 return [x, y];
8691             }
8692             if(vp){
8693                 var sc = this.getScroll();
8694                 return [x + sc.left, y + sc.top];
8695             }
8696             //Add the element's offset xy
8697             var o = this.getXY();
8698             return [x+o[0], y+o[1]];
8699         },
8700
8701         /**
8702          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8703          * supported position values.
8704          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8705          * @param {String} position The position to align to.
8706          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8707          * @return {Array} [x, y]
8708          */
8709         getAlignToXY : function(el, p, o)
8710         {
8711             el = Roo.get(el);
8712             var d = this.dom;
8713             if(!el.dom){
8714                 throw "Element.alignTo with an element that doesn't exist";
8715             }
8716             var c = false; //constrain to viewport
8717             var p1 = "", p2 = "";
8718             o = o || [0,0];
8719
8720             if(!p){
8721                 p = "tl-bl";
8722             }else if(p == "?"){
8723                 p = "tl-bl?";
8724             }else if(p.indexOf("-") == -1){
8725                 p = "tl-" + p;
8726             }
8727             p = p.toLowerCase();
8728             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8729             if(!m){
8730                throw "Element.alignTo with an invalid alignment " + p;
8731             }
8732             p1 = m[1]; p2 = m[2]; c = !!m[3];
8733
8734             //Subtract the aligned el's internal xy from the target's offset xy
8735             //plus custom offset to get the aligned el's new offset xy
8736             var a1 = this.getAnchorXY(p1, true);
8737             var a2 = el.getAnchorXY(p2, false);
8738             var x = a2[0] - a1[0] + o[0];
8739             var y = a2[1] - a1[1] + o[1];
8740             if(c){
8741                 //constrain the aligned el to viewport if necessary
8742                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8743                 // 5px of margin for ie
8744                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8745
8746                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8747                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8748                 //otherwise swap the aligned el to the opposite border of the target.
8749                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8750                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8751                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8752                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8753
8754                var doc = document;
8755                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8756                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8757
8758                if((x+w) > dw + scrollX){
8759                     x = swapX ? r.left-w : dw+scrollX-w;
8760                 }
8761                if(x < scrollX){
8762                    x = swapX ? r.right : scrollX;
8763                }
8764                if((y+h) > dh + scrollY){
8765                     y = swapY ? r.top-h : dh+scrollY-h;
8766                 }
8767                if (y < scrollY){
8768                    y = swapY ? r.bottom : scrollY;
8769                }
8770             }
8771             return [x,y];
8772         },
8773
8774         // private
8775         getConstrainToXY : function(){
8776             var os = {top:0, left:0, bottom:0, right: 0};
8777
8778             return function(el, local, offsets, proposedXY){
8779                 el = Roo.get(el);
8780                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8781
8782                 var vw, vh, vx = 0, vy = 0;
8783                 if(el.dom == document.body || el.dom == document){
8784                     vw = Roo.lib.Dom.getViewWidth();
8785                     vh = Roo.lib.Dom.getViewHeight();
8786                 }else{
8787                     vw = el.dom.clientWidth;
8788                     vh = el.dom.clientHeight;
8789                     if(!local){
8790                         var vxy = el.getXY();
8791                         vx = vxy[0];
8792                         vy = vxy[1];
8793                     }
8794                 }
8795
8796                 var s = el.getScroll();
8797
8798                 vx += offsets.left + s.left;
8799                 vy += offsets.top + s.top;
8800
8801                 vw -= offsets.right;
8802                 vh -= offsets.bottom;
8803
8804                 var vr = vx+vw;
8805                 var vb = vy+vh;
8806
8807                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8808                 var x = xy[0], y = xy[1];
8809                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8810
8811                 // only move it if it needs it
8812                 var moved = false;
8813
8814                 // first validate right/bottom
8815                 if((x + w) > vr){
8816                     x = vr - w;
8817                     moved = true;
8818                 }
8819                 if((y + h) > vb){
8820                     y = vb - h;
8821                     moved = true;
8822                 }
8823                 // then make sure top/left isn't negative
8824                 if(x < vx){
8825                     x = vx;
8826                     moved = true;
8827                 }
8828                 if(y < vy){
8829                     y = vy;
8830                     moved = true;
8831                 }
8832                 return moved ? [x, y] : false;
8833             };
8834         }(),
8835
8836         // private
8837         adjustForConstraints : function(xy, parent, offsets){
8838             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8839         },
8840
8841         /**
8842          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8843          * document it aligns it to the viewport.
8844          * The position parameter is optional, and can be specified in any one of the following formats:
8845          * <ul>
8846          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8847          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8848          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8849          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8850          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8851          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8852          * </ul>
8853          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8854          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8855          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8856          * that specified in order to enforce the viewport constraints.
8857          * Following are all of the supported anchor positions:
8858     <pre>
8859     Value  Description
8860     -----  -----------------------------
8861     tl     The top left corner (default)
8862     t      The center of the top edge
8863     tr     The top right corner
8864     l      The center of the left edge
8865     c      In the center of the element
8866     r      The center of the right edge
8867     bl     The bottom left corner
8868     b      The center of the bottom edge
8869     br     The bottom right corner
8870     </pre>
8871     Example Usage:
8872     <pre><code>
8873     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8874     el.alignTo("other-el");
8875
8876     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8877     el.alignTo("other-el", "tr?");
8878
8879     // align the bottom right corner of el with the center left edge of other-el
8880     el.alignTo("other-el", "br-l?");
8881
8882     // align the center of el with the bottom left corner of other-el and
8883     // adjust the x position by -6 pixels (and the y position by 0)
8884     el.alignTo("other-el", "c-bl", [-6, 0]);
8885     </code></pre>
8886          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8887          * @param {String} position The position to align to.
8888          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8889          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8890          * @return {Roo.Element} this
8891          */
8892         alignTo : function(element, position, offsets, animate){
8893             var xy = this.getAlignToXY(element, position, offsets);
8894             this.setXY(xy, this.preanim(arguments, 3));
8895             return this;
8896         },
8897
8898         /**
8899          * Anchors an element to another element and realigns it when the window is resized.
8900          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8901          * @param {String} position The position to align to.
8902          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8903          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8904          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8905          * is a number, it is used as the buffer delay (defaults to 50ms).
8906          * @param {Function} callback The function to call after the animation finishes
8907          * @return {Roo.Element} this
8908          */
8909         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8910             var action = function(){
8911                 this.alignTo(el, alignment, offsets, animate);
8912                 Roo.callback(callback, this);
8913             };
8914             Roo.EventManager.onWindowResize(action, this);
8915             var tm = typeof monitorScroll;
8916             if(tm != 'undefined'){
8917                 Roo.EventManager.on(window, 'scroll', action, this,
8918                     {buffer: tm == 'number' ? monitorScroll : 50});
8919             }
8920             action.call(this); // align immediately
8921             return this;
8922         },
8923         /**
8924          * Clears any opacity settings from this element. Required in some cases for IE.
8925          * @return {Roo.Element} this
8926          */
8927         clearOpacity : function(){
8928             if (window.ActiveXObject) {
8929                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8930                     this.dom.style.filter = "";
8931                 }
8932             } else {
8933                 this.dom.style.opacity = "";
8934                 this.dom.style["-moz-opacity"] = "";
8935                 this.dom.style["-khtml-opacity"] = "";
8936             }
8937             return this;
8938         },
8939
8940         /**
8941          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8942          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8943          * @return {Roo.Element} this
8944          */
8945         hide : function(animate){
8946             this.setVisible(false, this.preanim(arguments, 0));
8947             return this;
8948         },
8949
8950         /**
8951         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8952         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8953          * @return {Roo.Element} this
8954          */
8955         show : function(animate){
8956             this.setVisible(true, this.preanim(arguments, 0));
8957             return this;
8958         },
8959
8960         /**
8961          * @private Test if size has a unit, otherwise appends the default
8962          */
8963         addUnits : function(size){
8964             return Roo.Element.addUnits(size, this.defaultUnit);
8965         },
8966
8967         /**
8968          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8969          * @return {Roo.Element} this
8970          */
8971         beginMeasure : function(){
8972             var el = this.dom;
8973             if(el.offsetWidth || el.offsetHeight){
8974                 return this; // offsets work already
8975             }
8976             var changed = [];
8977             var p = this.dom, b = document.body; // start with this element
8978             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8979                 var pe = Roo.get(p);
8980                 if(pe.getStyle('display') == 'none'){
8981                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8982                     p.style.visibility = "hidden";
8983                     p.style.display = "block";
8984                 }
8985                 p = p.parentNode;
8986             }
8987             this._measureChanged = changed;
8988             return this;
8989
8990         },
8991
8992         /**
8993          * Restores displays to before beginMeasure was called
8994          * @return {Roo.Element} this
8995          */
8996         endMeasure : function(){
8997             var changed = this._measureChanged;
8998             if(changed){
8999                 for(var i = 0, len = changed.length; i < len; i++) {
9000                     var r = changed[i];
9001                     r.el.style.visibility = r.visibility;
9002                     r.el.style.display = "none";
9003                 }
9004                 this._measureChanged = null;
9005             }
9006             return this;
9007         },
9008
9009         /**
9010         * Update the innerHTML of this element, optionally searching for and processing scripts
9011         * @param {String} html The new HTML
9012         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9013         * @param {Function} callback For async script loading you can be noticed when the update completes
9014         * @return {Roo.Element} this
9015          */
9016         update : function(html, loadScripts, callback){
9017             if(typeof html == "undefined"){
9018                 html = "";
9019             }
9020             if(loadScripts !== true){
9021                 this.dom.innerHTML = html;
9022                 if(typeof callback == "function"){
9023                     callback();
9024                 }
9025                 return this;
9026             }
9027             var id = Roo.id();
9028             var dom = this.dom;
9029
9030             html += '<span id="' + id + '"></span>';
9031
9032             E.onAvailable(id, function(){
9033                 var hd = document.getElementsByTagName("head")[0];
9034                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9035                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9036                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9037
9038                 var match;
9039                 while(match = re.exec(html)){
9040                     var attrs = match[1];
9041                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9042                     if(srcMatch && srcMatch[2]){
9043                        var s = document.createElement("script");
9044                        s.src = srcMatch[2];
9045                        var typeMatch = attrs.match(typeRe);
9046                        if(typeMatch && typeMatch[2]){
9047                            s.type = typeMatch[2];
9048                        }
9049                        hd.appendChild(s);
9050                     }else if(match[2] && match[2].length > 0){
9051                         if(window.execScript) {
9052                            window.execScript(match[2]);
9053                         } else {
9054                             /**
9055                              * eval:var:id
9056                              * eval:var:dom
9057                              * eval:var:html
9058                              * 
9059                              */
9060                            window.eval(match[2]);
9061                         }
9062                     }
9063                 }
9064                 var el = document.getElementById(id);
9065                 if(el){el.parentNode.removeChild(el);}
9066                 if(typeof callback == "function"){
9067                     callback();
9068                 }
9069             });
9070             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9071             return this;
9072         },
9073
9074         /**
9075          * Direct access to the UpdateManager update() method (takes the same parameters).
9076          * @param {String/Function} url The url for this request or a function to call to get the url
9077          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9078          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9079          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
9080          * @return {Roo.Element} this
9081          */
9082         load : function(){
9083             var um = this.getUpdateManager();
9084             um.update.apply(um, arguments);
9085             return this;
9086         },
9087
9088         /**
9089         * Gets this element's UpdateManager
9090         * @return {Roo.UpdateManager} The UpdateManager
9091         */
9092         getUpdateManager : function(){
9093             if(!this.updateManager){
9094                 this.updateManager = new Roo.UpdateManager(this);
9095             }
9096             return this.updateManager;
9097         },
9098
9099         /**
9100          * Disables text selection for this element (normalized across browsers)
9101          * @return {Roo.Element} this
9102          */
9103         unselectable : function(){
9104             this.dom.unselectable = "on";
9105             this.swallowEvent("selectstart", true);
9106             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9107             this.addClass("x-unselectable");
9108             return this;
9109         },
9110
9111         /**
9112         * Calculates the x, y to center this element on the screen
9113         * @return {Array} The x, y values [x, y]
9114         */
9115         getCenterXY : function(){
9116             return this.getAlignToXY(document, 'c-c');
9117         },
9118
9119         /**
9120         * Centers the Element in either the viewport, or another Element.
9121         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9122         */
9123         center : function(centerIn){
9124             this.alignTo(centerIn || document, 'c-c');
9125             return this;
9126         },
9127
9128         /**
9129          * Tests various css rules/browsers to determine if this element uses a border box
9130          * @return {Boolean}
9131          */
9132         isBorderBox : function(){
9133             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9134         },
9135
9136         /**
9137          * Return a box {x, y, width, height} that can be used to set another elements
9138          * size/location to match this element.
9139          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9140          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9141          * @return {Object} box An object in the format {x, y, width, height}
9142          */
9143         getBox : function(contentBox, local){
9144             var xy;
9145             if(!local){
9146                 xy = this.getXY();
9147             }else{
9148                 var left = parseInt(this.getStyle("left"), 10) || 0;
9149                 var top = parseInt(this.getStyle("top"), 10) || 0;
9150                 xy = [left, top];
9151             }
9152             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9153             if(!contentBox){
9154                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9155             }else{
9156                 var l = this.getBorderWidth("l")+this.getPadding("l");
9157                 var r = this.getBorderWidth("r")+this.getPadding("r");
9158                 var t = this.getBorderWidth("t")+this.getPadding("t");
9159                 var b = this.getBorderWidth("b")+this.getPadding("b");
9160                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9161             }
9162             bx.right = bx.x + bx.width;
9163             bx.bottom = bx.y + bx.height;
9164             return bx;
9165         },
9166
9167         /**
9168          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9169          for more information about the sides.
9170          * @param {String} sides
9171          * @return {Number}
9172          */
9173         getFrameWidth : function(sides, onlyContentBox){
9174             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9175         },
9176
9177         /**
9178          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9179          * @param {Object} box The box to fill {x, y, width, height}
9180          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9181          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9182          * @return {Roo.Element} this
9183          */
9184         setBox : function(box, adjust, animate){
9185             var w = box.width, h = box.height;
9186             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9187                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9188                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9189             }
9190             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9191             return this;
9192         },
9193
9194         /**
9195          * Forces the browser to repaint this element
9196          * @return {Roo.Element} this
9197          */
9198          repaint : function(){
9199             var dom = this.dom;
9200             this.addClass("x-repaint");
9201             setTimeout(function(){
9202                 Roo.get(dom).removeClass("x-repaint");
9203             }, 1);
9204             return this;
9205         },
9206
9207         /**
9208          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9209          * then it returns the calculated width of the sides (see getPadding)
9210          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9211          * @return {Object/Number}
9212          */
9213         getMargins : function(side){
9214             if(!side){
9215                 return {
9216                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9217                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9218                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9219                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9220                 };
9221             }else{
9222                 return this.addStyles(side, El.margins);
9223              }
9224         },
9225
9226         // private
9227         addStyles : function(sides, styles){
9228             var val = 0, v, w;
9229             for(var i = 0, len = sides.length; i < len; i++){
9230                 v = this.getStyle(styles[sides.charAt(i)]);
9231                 if(v){
9232                      w = parseInt(v, 10);
9233                      if(w){ val += w; }
9234                 }
9235             }
9236             return val;
9237         },
9238
9239         /**
9240          * Creates a proxy element of this element
9241          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9242          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9243          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9244          * @return {Roo.Element} The new proxy element
9245          */
9246         createProxy : function(config, renderTo, matchBox){
9247             if(renderTo){
9248                 renderTo = Roo.getDom(renderTo);
9249             }else{
9250                 renderTo = document.body;
9251             }
9252             config = typeof config == "object" ?
9253                 config : {tag : "div", cls: config};
9254             var proxy = Roo.DomHelper.append(renderTo, config, true);
9255             if(matchBox){
9256                proxy.setBox(this.getBox());
9257             }
9258             return proxy;
9259         },
9260
9261         /**
9262          * Puts a mask over this element to disable user interaction. Requires core.css.
9263          * This method can only be applied to elements which accept child nodes.
9264          * @param {String} msg (optional) A message to display in the mask
9265          * @param {String} msgCls (optional) A css class to apply to the msg element
9266          * @return {Element} The mask  element
9267          */
9268         mask : function(msg, msgCls)
9269         {
9270             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9271                 this.setStyle("position", "relative");
9272             }
9273             if(!this._mask){
9274                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9275             }
9276             
9277             this.addClass("x-masked");
9278             this._mask.setDisplayed(true);
9279             
9280             // we wander
9281             var z = 0;
9282             var dom = this.dom;
9283             while (dom && dom.style) {
9284                 if (!isNaN(parseInt(dom.style.zIndex))) {
9285                     z = Math.max(z, parseInt(dom.style.zIndex));
9286                 }
9287                 dom = dom.parentNode;
9288             }
9289             // if we are masking the body - then it hides everything..
9290             if (this.dom == document.body) {
9291                 z = 1000000;
9292                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9293                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9294             }
9295            
9296             if(typeof msg == 'string'){
9297                 if(!this._maskMsg){
9298                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9299                         cls: "roo-el-mask-msg", 
9300                         cn: [
9301                             {
9302                                 tag: 'i',
9303                                 cls: 'fa fa-spinner fa-spin'
9304                             },
9305                             {
9306                                 tag: 'div'
9307                             }   
9308                         ]
9309                     }, true);
9310                 }
9311                 var mm = this._maskMsg;
9312                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9313                 if (mm.dom.lastChild) { // weird IE issue?
9314                     mm.dom.lastChild.innerHTML = msg;
9315                 }
9316                 mm.setDisplayed(true);
9317                 mm.center(this);
9318                 mm.setStyle('z-index', z + 102);
9319             }
9320             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9321                 this._mask.setHeight(this.getHeight());
9322             }
9323             this._mask.setStyle('z-index', z + 100);
9324             
9325             return this._mask;
9326         },
9327
9328         /**
9329          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9330          * it is cached for reuse.
9331          */
9332         unmask : function(removeEl){
9333             if(this._mask){
9334                 if(removeEl === true){
9335                     this._mask.remove();
9336                     delete this._mask;
9337                     if(this._maskMsg){
9338                         this._maskMsg.remove();
9339                         delete this._maskMsg;
9340                     }
9341                 }else{
9342                     this._mask.setDisplayed(false);
9343                     if(this._maskMsg){
9344                         this._maskMsg.setDisplayed(false);
9345                     }
9346                 }
9347             }
9348             this.removeClass("x-masked");
9349         },
9350
9351         /**
9352          * Returns true if this element is masked
9353          * @return {Boolean}
9354          */
9355         isMasked : function(){
9356             return this._mask && this._mask.isVisible();
9357         },
9358
9359         /**
9360          * Creates an iframe shim for this element to keep selects and other windowed objects from
9361          * showing through.
9362          * @return {Roo.Element} The new shim element
9363          */
9364         createShim : function(){
9365             var el = document.createElement('iframe');
9366             el.frameBorder = 'no';
9367             el.className = 'roo-shim';
9368             if(Roo.isIE && Roo.isSecure){
9369                 el.src = Roo.SSL_SECURE_URL;
9370             }
9371             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9372             shim.autoBoxAdjust = false;
9373             return shim;
9374         },
9375
9376         /**
9377          * Removes this element from the DOM and deletes it from the cache
9378          */
9379         remove : function(){
9380             if(this.dom.parentNode){
9381                 this.dom.parentNode.removeChild(this.dom);
9382             }
9383             delete El.cache[this.dom.id];
9384         },
9385
9386         /**
9387          * Sets up event handlers to add and remove a css class when the mouse is over this element
9388          * @param {String} className
9389          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9390          * mouseout events for children elements
9391          * @return {Roo.Element} this
9392          */
9393         addClassOnOver : function(className, preventFlicker){
9394             this.on("mouseover", function(){
9395                 Roo.fly(this, '_internal').addClass(className);
9396             }, this.dom);
9397             var removeFn = function(e){
9398                 if(preventFlicker !== true || !e.within(this, true)){
9399                     Roo.fly(this, '_internal').removeClass(className);
9400                 }
9401             };
9402             this.on("mouseout", removeFn, this.dom);
9403             return this;
9404         },
9405
9406         /**
9407          * Sets up event handlers to add and remove a css class when this element has the focus
9408          * @param {String} className
9409          * @return {Roo.Element} this
9410          */
9411         addClassOnFocus : function(className){
9412             this.on("focus", function(){
9413                 Roo.fly(this, '_internal').addClass(className);
9414             }, this.dom);
9415             this.on("blur", function(){
9416                 Roo.fly(this, '_internal').removeClass(className);
9417             }, this.dom);
9418             return this;
9419         },
9420         /**
9421          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9422          * @param {String} className
9423          * @return {Roo.Element} this
9424          */
9425         addClassOnClick : function(className){
9426             var dom = this.dom;
9427             this.on("mousedown", function(){
9428                 Roo.fly(dom, '_internal').addClass(className);
9429                 var d = Roo.get(document);
9430                 var fn = function(){
9431                     Roo.fly(dom, '_internal').removeClass(className);
9432                     d.removeListener("mouseup", fn);
9433                 };
9434                 d.on("mouseup", fn);
9435             });
9436             return this;
9437         },
9438
9439         /**
9440          * Stops the specified event from bubbling and optionally prevents the default action
9441          * @param {String} eventName
9442          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9443          * @return {Roo.Element} this
9444          */
9445         swallowEvent : function(eventName, preventDefault){
9446             var fn = function(e){
9447                 e.stopPropagation();
9448                 if(preventDefault){
9449                     e.preventDefault();
9450                 }
9451             };
9452             if(eventName instanceof Array){
9453                 for(var i = 0, len = eventName.length; i < len; i++){
9454                      this.on(eventName[i], fn);
9455                 }
9456                 return this;
9457             }
9458             this.on(eventName, fn);
9459             return this;
9460         },
9461
9462         /**
9463          * @private
9464          */
9465         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9466
9467         /**
9468          * Sizes this element to its parent element's dimensions performing
9469          * neccessary box adjustments.
9470          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9471          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9472          * @return {Roo.Element} this
9473          */
9474         fitToParent : function(monitorResize, targetParent) {
9475           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9476           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9477           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9478             return this;
9479           }
9480           var p = Roo.get(targetParent || this.dom.parentNode);
9481           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9482           if (monitorResize === true) {
9483             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9484             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9485           }
9486           return this;
9487         },
9488
9489         /**
9490          * Gets the next sibling, skipping text nodes
9491          * @return {HTMLElement} The next sibling or null
9492          */
9493         getNextSibling : function(){
9494             var n = this.dom.nextSibling;
9495             while(n && n.nodeType != 1){
9496                 n = n.nextSibling;
9497             }
9498             return n;
9499         },
9500
9501         /**
9502          * Gets the previous sibling, skipping text nodes
9503          * @return {HTMLElement} The previous sibling or null
9504          */
9505         getPrevSibling : function(){
9506             var n = this.dom.previousSibling;
9507             while(n && n.nodeType != 1){
9508                 n = n.previousSibling;
9509             }
9510             return n;
9511         },
9512
9513
9514         /**
9515          * Appends the passed element(s) to this element
9516          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9517          * @return {Roo.Element} this
9518          */
9519         appendChild: function(el){
9520             el = Roo.get(el);
9521             el.appendTo(this);
9522             return this;
9523         },
9524
9525         /**
9526          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9527          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9528          * automatically generated with the specified attributes.
9529          * @param {HTMLElement} insertBefore (optional) a child element of this element
9530          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9531          * @return {Roo.Element} The new child element
9532          */
9533         createChild: function(config, insertBefore, returnDom){
9534             config = config || {tag:'div'};
9535             if(insertBefore){
9536                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9537             }
9538             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9539         },
9540
9541         /**
9542          * Appends this element to the passed element
9543          * @param {String/HTMLElement/Element} el The new parent element
9544          * @return {Roo.Element} this
9545          */
9546         appendTo: function(el){
9547             el = Roo.getDom(el);
9548             el.appendChild(this.dom);
9549             return this;
9550         },
9551
9552         /**
9553          * Inserts this element before the passed element in the DOM
9554          * @param {String/HTMLElement/Element} el The element to insert before
9555          * @return {Roo.Element} this
9556          */
9557         insertBefore: function(el){
9558             el = Roo.getDom(el);
9559             el.parentNode.insertBefore(this.dom, el);
9560             return this;
9561         },
9562
9563         /**
9564          * Inserts this element after the passed element in the DOM
9565          * @param {String/HTMLElement/Element} el The element to insert after
9566          * @return {Roo.Element} this
9567          */
9568         insertAfter: function(el){
9569             el = Roo.getDom(el);
9570             el.parentNode.insertBefore(this.dom, el.nextSibling);
9571             return this;
9572         },
9573
9574         /**
9575          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9576          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9577          * @return {Roo.Element} The new child
9578          */
9579         insertFirst: function(el, returnDom){
9580             el = el || {};
9581             if(typeof el == 'object' && !el.nodeType){ // dh config
9582                 return this.createChild(el, this.dom.firstChild, returnDom);
9583             }else{
9584                 el = Roo.getDom(el);
9585                 this.dom.insertBefore(el, this.dom.firstChild);
9586                 return !returnDom ? Roo.get(el) : el;
9587             }
9588         },
9589
9590         /**
9591          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9592          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9593          * @param {String} where (optional) 'before' or 'after' defaults to before
9594          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9595          * @return {Roo.Element} the inserted Element
9596          */
9597         insertSibling: function(el, where, returnDom){
9598             where = where ? where.toLowerCase() : 'before';
9599             el = el || {};
9600             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9601
9602             if(typeof el == 'object' && !el.nodeType){ // dh config
9603                 if(where == 'after' && !this.dom.nextSibling){
9604                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9605                 }else{
9606                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9607                 }
9608
9609             }else{
9610                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9611                             where == 'before' ? this.dom : this.dom.nextSibling);
9612                 if(!returnDom){
9613                     rt = Roo.get(rt);
9614                 }
9615             }
9616             return rt;
9617         },
9618
9619         /**
9620          * Creates and wraps this element with another element
9621          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9622          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9623          * @return {HTMLElement/Element} The newly created wrapper element
9624          */
9625         wrap: function(config, returnDom){
9626             if(!config){
9627                 config = {tag: "div"};
9628             }
9629             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9630             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9631             return newEl;
9632         },
9633
9634         /**
9635          * Replaces the passed element with this element
9636          * @param {String/HTMLElement/Element} el The element to replace
9637          * @return {Roo.Element} this
9638          */
9639         replace: function(el){
9640             el = Roo.get(el);
9641             this.insertBefore(el);
9642             el.remove();
9643             return this;
9644         },
9645
9646         /**
9647          * Inserts an html fragment into this element
9648          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9649          * @param {String} html The HTML fragment
9650          * @param {Boolean} returnEl True to return an Roo.Element
9651          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9652          */
9653         insertHtml : function(where, html, returnEl){
9654             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9655             return returnEl ? Roo.get(el) : el;
9656         },
9657
9658         /**
9659          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9660          * @param {Object} o The object with the attributes
9661          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9662          * @return {Roo.Element} this
9663          */
9664         set : function(o, useSet){
9665             var el = this.dom;
9666             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9667             for(var attr in o){
9668                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9669                 if(attr=="cls"){
9670                     el.className = o["cls"];
9671                 }else{
9672                     if(useSet) {
9673                         el.setAttribute(attr, o[attr]);
9674                     } else {
9675                         el[attr] = o[attr];
9676                     }
9677                 }
9678             }
9679             if(o.style){
9680                 Roo.DomHelper.applyStyles(el, o.style);
9681             }
9682             return this;
9683         },
9684
9685         /**
9686          * Convenience method for constructing a KeyMap
9687          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9688          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9689          * @param {Function} fn The function to call
9690          * @param {Object} scope (optional) The scope of the function
9691          * @return {Roo.KeyMap} The KeyMap created
9692          */
9693         addKeyListener : function(key, fn, scope){
9694             var config;
9695             if(typeof key != "object" || key instanceof Array){
9696                 config = {
9697                     key: key,
9698                     fn: fn,
9699                     scope: scope
9700                 };
9701             }else{
9702                 config = {
9703                     key : key.key,
9704                     shift : key.shift,
9705                     ctrl : key.ctrl,
9706                     alt : key.alt,
9707                     fn: fn,
9708                     scope: scope
9709                 };
9710             }
9711             return new Roo.KeyMap(this, config);
9712         },
9713
9714         /**
9715          * Creates a KeyMap for this element
9716          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9717          * @return {Roo.KeyMap} The KeyMap created
9718          */
9719         addKeyMap : function(config){
9720             return new Roo.KeyMap(this, config);
9721         },
9722
9723         /**
9724          * Returns true if this element is scrollable.
9725          * @return {Boolean}
9726          */
9727          isScrollable : function(){
9728             var dom = this.dom;
9729             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9730         },
9731
9732         /**
9733          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9734          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9735          * @param {Number} value The new scroll value
9736          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9737          * @return {Element} this
9738          */
9739
9740         scrollTo : function(side, value, animate){
9741             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9742             if(!animate || !A){
9743                 this.dom[prop] = value;
9744             }else{
9745                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9746                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9747             }
9748             return this;
9749         },
9750
9751         /**
9752          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9753          * within this element's scrollable range.
9754          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9755          * @param {Number} distance How far to scroll the element in pixels
9756          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9757          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9758          * was scrolled as far as it could go.
9759          */
9760          scroll : function(direction, distance, animate){
9761              if(!this.isScrollable()){
9762                  return;
9763              }
9764              var el = this.dom;
9765              var l = el.scrollLeft, t = el.scrollTop;
9766              var w = el.scrollWidth, h = el.scrollHeight;
9767              var cw = el.clientWidth, ch = el.clientHeight;
9768              direction = direction.toLowerCase();
9769              var scrolled = false;
9770              var a = this.preanim(arguments, 2);
9771              switch(direction){
9772                  case "l":
9773                  case "left":
9774                      if(w - l > cw){
9775                          var v = Math.min(l + distance, w-cw);
9776                          this.scrollTo("left", v, a);
9777                          scrolled = true;
9778                      }
9779                      break;
9780                 case "r":
9781                 case "right":
9782                      if(l > 0){
9783                          var v = Math.max(l - distance, 0);
9784                          this.scrollTo("left", v, a);
9785                          scrolled = true;
9786                      }
9787                      break;
9788                 case "t":
9789                 case "top":
9790                 case "up":
9791                      if(t > 0){
9792                          var v = Math.max(t - distance, 0);
9793                          this.scrollTo("top", v, a);
9794                          scrolled = true;
9795                      }
9796                      break;
9797                 case "b":
9798                 case "bottom":
9799                 case "down":
9800                      if(h - t > ch){
9801                          var v = Math.min(t + distance, h-ch);
9802                          this.scrollTo("top", v, a);
9803                          scrolled = true;
9804                      }
9805                      break;
9806              }
9807              return scrolled;
9808         },
9809
9810         /**
9811          * Translates the passed page coordinates into left/top css values for this element
9812          * @param {Number/Array} x The page x or an array containing [x, y]
9813          * @param {Number} y The page y
9814          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9815          */
9816         translatePoints : function(x, y){
9817             if(typeof x == 'object' || x instanceof Array){
9818                 y = x[1]; x = x[0];
9819             }
9820             var p = this.getStyle('position');
9821             var o = this.getXY();
9822
9823             var l = parseInt(this.getStyle('left'), 10);
9824             var t = parseInt(this.getStyle('top'), 10);
9825
9826             if(isNaN(l)){
9827                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9828             }
9829             if(isNaN(t)){
9830                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9831             }
9832
9833             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9834         },
9835
9836         /**
9837          * Returns the current scroll position of the element.
9838          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9839          */
9840         getScroll : function(){
9841             var d = this.dom, doc = document;
9842             if(d == doc || d == doc.body){
9843                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9844                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9845                 return {left: l, top: t};
9846             }else{
9847                 return {left: d.scrollLeft, top: d.scrollTop};
9848             }
9849         },
9850
9851         /**
9852          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9853          * are convert to standard 6 digit hex color.
9854          * @param {String} attr The css attribute
9855          * @param {String} defaultValue The default value to use when a valid color isn't found
9856          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9857          * YUI color anims.
9858          */
9859         getColor : function(attr, defaultValue, prefix){
9860             var v = this.getStyle(attr);
9861             if(!v || v == "transparent" || v == "inherit") {
9862                 return defaultValue;
9863             }
9864             var color = typeof prefix == "undefined" ? "#" : prefix;
9865             if(v.substr(0, 4) == "rgb("){
9866                 var rvs = v.slice(4, v.length -1).split(",");
9867                 for(var i = 0; i < 3; i++){
9868                     var h = parseInt(rvs[i]).toString(16);
9869                     if(h < 16){
9870                         h = "0" + h;
9871                     }
9872                     color += h;
9873                 }
9874             } else {
9875                 if(v.substr(0, 1) == "#"){
9876                     if(v.length == 4) {
9877                         for(var i = 1; i < 4; i++){
9878                             var c = v.charAt(i);
9879                             color +=  c + c;
9880                         }
9881                     }else if(v.length == 7){
9882                         color += v.substr(1);
9883                     }
9884                 }
9885             }
9886             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9887         },
9888
9889         /**
9890          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9891          * gradient background, rounded corners and a 4-way shadow.
9892          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9893          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9894          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9895          * @return {Roo.Element} this
9896          */
9897         boxWrap : function(cls){
9898             cls = cls || 'x-box';
9899             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9900             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9901             return el;
9902         },
9903
9904         /**
9905          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9906          * @param {String} namespace The namespace in which to look for the attribute
9907          * @param {String} name The attribute name
9908          * @return {String} The attribute value
9909          */
9910         getAttributeNS : Roo.isIE ? function(ns, name){
9911             var d = this.dom;
9912             var type = typeof d[ns+":"+name];
9913             if(type != 'undefined' && type != 'unknown'){
9914                 return d[ns+":"+name];
9915             }
9916             return d[name];
9917         } : function(ns, name){
9918             var d = this.dom;
9919             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9920         },
9921         
9922         
9923         /**
9924          * Sets or Returns the value the dom attribute value
9925          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9926          * @param {String} value (optional) The value to set the attribute to
9927          * @return {String} The attribute value
9928          */
9929         attr : function(name){
9930             if (arguments.length > 1) {
9931                 this.dom.setAttribute(name, arguments[1]);
9932                 return arguments[1];
9933             }
9934             if (typeof(name) == 'object') {
9935                 for(var i in name) {
9936                     this.attr(i, name[i]);
9937                 }
9938                 return name;
9939             }
9940             
9941             
9942             if (!this.dom.hasAttribute(name)) {
9943                 return undefined;
9944             }
9945             return this.dom.getAttribute(name);
9946         }
9947         
9948         
9949         
9950     };
9951
9952     var ep = El.prototype;
9953
9954     /**
9955      * Appends an event handler (Shorthand for addListener)
9956      * @param {String}   eventName     The type of event to append
9957      * @param {Function} fn        The method the event invokes
9958      * @param {Object} scope       (optional) The scope (this object) of the fn
9959      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9960      * @method
9961      */
9962     ep.on = ep.addListener;
9963         // backwards compat
9964     ep.mon = ep.addListener;
9965
9966     /**
9967      * Removes an event handler from this element (shorthand for removeListener)
9968      * @param {String} eventName the type of event to remove
9969      * @param {Function} fn the method the event invokes
9970      * @return {Roo.Element} this
9971      * @method
9972      */
9973     ep.un = ep.removeListener;
9974
9975     /**
9976      * true to automatically adjust width and height settings for box-model issues (default to true)
9977      */
9978     ep.autoBoxAdjust = true;
9979
9980     // private
9981     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9982
9983     // private
9984     El.addUnits = function(v, defaultUnit){
9985         if(v === "" || v == "auto"){
9986             return v;
9987         }
9988         if(v === undefined){
9989             return '';
9990         }
9991         if(typeof v == "number" || !El.unitPattern.test(v)){
9992             return v + (defaultUnit || 'px');
9993         }
9994         return v;
9995     };
9996
9997     // special markup used throughout Roo when box wrapping elements
9998     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9999     /**
10000      * Visibility mode constant - Use visibility to hide element
10001      * @static
10002      * @type Number
10003      */
10004     El.VISIBILITY = 1;
10005     /**
10006      * Visibility mode constant - Use display to hide element
10007      * @static
10008      * @type Number
10009      */
10010     El.DISPLAY = 2;
10011
10012     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10013     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10014     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10015
10016
10017
10018     /**
10019      * @private
10020      */
10021     El.cache = {};
10022
10023     var docEl;
10024
10025     /**
10026      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10027      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10028      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10029      * @return {Element} The Element object
10030      * @static
10031      */
10032     El.get = function(el){
10033         var ex, elm, id;
10034         if(!el){ return null; }
10035         if(typeof el == "string"){ // element id
10036             if(!(elm = document.getElementById(el))){
10037                 return null;
10038             }
10039             if(ex = El.cache[el]){
10040                 ex.dom = elm;
10041             }else{
10042                 ex = El.cache[el] = new El(elm);
10043             }
10044             return ex;
10045         }else if(el.tagName){ // dom element
10046             if(!(id = el.id)){
10047                 id = Roo.id(el);
10048             }
10049             if(ex = El.cache[id]){
10050                 ex.dom = el;
10051             }else{
10052                 ex = El.cache[id] = new El(el);
10053             }
10054             return ex;
10055         }else if(el instanceof El){
10056             if(el != docEl){
10057                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10058                                                               // catch case where it hasn't been appended
10059                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10060             }
10061             return el;
10062         }else if(el.isComposite){
10063             return el;
10064         }else if(el instanceof Array){
10065             return El.select(el);
10066         }else if(el == document){
10067             // create a bogus element object representing the document object
10068             if(!docEl){
10069                 var f = function(){};
10070                 f.prototype = El.prototype;
10071                 docEl = new f();
10072                 docEl.dom = document;
10073             }
10074             return docEl;
10075         }
10076         return null;
10077     };
10078
10079     // private
10080     El.uncache = function(el){
10081         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10082             if(a[i]){
10083                 delete El.cache[a[i].id || a[i]];
10084             }
10085         }
10086     };
10087
10088     // private
10089     // Garbage collection - uncache elements/purge listeners on orphaned elements
10090     // so we don't hold a reference and cause the browser to retain them
10091     El.garbageCollect = function(){
10092         if(!Roo.enableGarbageCollector){
10093             clearInterval(El.collectorThread);
10094             return;
10095         }
10096         for(var eid in El.cache){
10097             var el = El.cache[eid], d = el.dom;
10098             // -------------------------------------------------------
10099             // Determining what is garbage:
10100             // -------------------------------------------------------
10101             // !d
10102             // dom node is null, definitely garbage
10103             // -------------------------------------------------------
10104             // !d.parentNode
10105             // no parentNode == direct orphan, definitely garbage
10106             // -------------------------------------------------------
10107             // !d.offsetParent && !document.getElementById(eid)
10108             // display none elements have no offsetParent so we will
10109             // also try to look it up by it's id. However, check
10110             // offsetParent first so we don't do unneeded lookups.
10111             // This enables collection of elements that are not orphans
10112             // directly, but somewhere up the line they have an orphan
10113             // parent.
10114             // -------------------------------------------------------
10115             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10116                 delete El.cache[eid];
10117                 if(d && Roo.enableListenerCollection){
10118                     E.purgeElement(d);
10119                 }
10120             }
10121         }
10122     }
10123     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10124
10125
10126     // dom is optional
10127     El.Flyweight = function(dom){
10128         this.dom = dom;
10129     };
10130     El.Flyweight.prototype = El.prototype;
10131
10132     El._flyweights = {};
10133     /**
10134      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10135      * the dom node can be overwritten by other code.
10136      * @param {String/HTMLElement} el The dom node or id
10137      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10138      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10139      * @static
10140      * @return {Element} The shared Element object
10141      */
10142     El.fly = function(el, named){
10143         named = named || '_global';
10144         el = Roo.getDom(el);
10145         if(!el){
10146             return null;
10147         }
10148         if(!El._flyweights[named]){
10149             El._flyweights[named] = new El.Flyweight();
10150         }
10151         El._flyweights[named].dom = el;
10152         return El._flyweights[named];
10153     };
10154
10155     /**
10156      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10157      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10158      * Shorthand of {@link Roo.Element#get}
10159      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10160      * @return {Element} The Element object
10161      * @member Roo
10162      * @method get
10163      */
10164     Roo.get = El.get;
10165     /**
10166      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10167      * the dom node can be overwritten by other code.
10168      * Shorthand of {@link Roo.Element#fly}
10169      * @param {String/HTMLElement} el The dom node or id
10170      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10171      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10172      * @static
10173      * @return {Element} The shared Element object
10174      * @member Roo
10175      * @method fly
10176      */
10177     Roo.fly = El.fly;
10178
10179     // speedy lookup for elements never to box adjust
10180     var noBoxAdjust = Roo.isStrict ? {
10181         select:1
10182     } : {
10183         input:1, select:1, textarea:1
10184     };
10185     if(Roo.isIE || Roo.isGecko){
10186         noBoxAdjust['button'] = 1;
10187     }
10188
10189
10190     Roo.EventManager.on(window, 'unload', function(){
10191         delete El.cache;
10192         delete El._flyweights;
10193     });
10194 })();
10195
10196
10197
10198
10199 if(Roo.DomQuery){
10200     Roo.Element.selectorFunction = Roo.DomQuery.select;
10201 }
10202
10203 Roo.Element.select = function(selector, unique, root){
10204     var els;
10205     if(typeof selector == "string"){
10206         els = Roo.Element.selectorFunction(selector, root);
10207     }else if(selector.length !== undefined){
10208         els = selector;
10209     }else{
10210         throw "Invalid selector";
10211     }
10212     if(unique === true){
10213         return new Roo.CompositeElement(els);
10214     }else{
10215         return new Roo.CompositeElementLite(els);
10216     }
10217 };
10218 /**
10219  * Selects elements based on the passed CSS selector to enable working on them as 1.
10220  * @param {String/Array} selector The CSS selector or an array of elements
10221  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10222  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10223  * @return {CompositeElementLite/CompositeElement}
10224  * @member Roo
10225  * @method select
10226  */
10227 Roo.select = Roo.Element.select;
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242 /*
10243  * Based on:
10244  * Ext JS Library 1.1.1
10245  * Copyright(c) 2006-2007, Ext JS, LLC.
10246  *
10247  * Originally Released Under LGPL - original licence link has changed is not relivant.
10248  *
10249  * Fork - LGPL
10250  * <script type="text/javascript">
10251  */
10252
10253
10254
10255 //Notifies Element that fx methods are available
10256 Roo.enableFx = true;
10257
10258 /**
10259  * @class Roo.Fx
10260  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10261  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10262  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10263  * Element effects to work.</p><br/>
10264  *
10265  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10266  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10267  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10268  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10269  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10270  * expected results and should be done with care.</p><br/>
10271  *
10272  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10273  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10274 <pre>
10275 Value  Description
10276 -----  -----------------------------
10277 tl     The top left corner
10278 t      The center of the top edge
10279 tr     The top right corner
10280 l      The center of the left edge
10281 r      The center of the right edge
10282 bl     The bottom left corner
10283 b      The center of the bottom edge
10284 br     The bottom right corner
10285 </pre>
10286  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10287  * below are common options that can be passed to any Fx method.</b>
10288  * @cfg {Function} callback A function called when the effect is finished
10289  * @cfg {Object} scope The scope of the effect function
10290  * @cfg {String} easing A valid Easing value for the effect
10291  * @cfg {String} afterCls A css class to apply after the effect
10292  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10293  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10294  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10295  * effects that end with the element being visually hidden, ignored otherwise)
10296  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10297  * a function which returns such a specification that will be applied to the Element after the effect finishes
10298  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10299  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10300  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10301  */
10302 Roo.Fx = {
10303         /**
10304          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10305          * origin for the slide effect.  This function automatically handles wrapping the element with
10306          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10307          * Usage:
10308          *<pre><code>
10309 // default: slide the element in from the top
10310 el.slideIn();
10311
10312 // custom: slide the element in from the right with a 2-second duration
10313 el.slideIn('r', { duration: 2 });
10314
10315 // common config options shown with default values
10316 el.slideIn('t', {
10317     easing: 'easeOut',
10318     duration: .5
10319 });
10320 </code></pre>
10321          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10322          * @param {Object} options (optional) Object literal with any of the Fx config options
10323          * @return {Roo.Element} The Element
10324          */
10325     slideIn : function(anchor, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330
10331             anchor = anchor || "t";
10332
10333             // fix display to visibility
10334             this.fixDisplay();
10335
10336             // restore values after effect
10337             var r = this.getFxRestore();
10338             var b = this.getBox();
10339             // fixed size for slide
10340             this.setSize(b);
10341
10342             // wrap if needed
10343             var wrap = this.fxWrap(r.pos, o, "hidden");
10344
10345             var st = this.dom.style;
10346             st.visibility = "visible";
10347             st.position = "absolute";
10348
10349             // clear out temp styles after slide and unwrap
10350             var after = function(){
10351                 el.fxUnwrap(wrap, r.pos, o);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354                 el.afterFx(o);
10355             };
10356             // time to calc the positions
10357             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10358
10359             switch(anchor.toLowerCase()){
10360                 case "t":
10361                     wrap.setSize(b.width, 0);
10362                     st.left = st.bottom = "0";
10363                     a = {height: bh};
10364                 break;
10365                 case "l":
10366                     wrap.setSize(0, b.height);
10367                     st.right = st.top = "0";
10368                     a = {width: bw};
10369                 break;
10370                 case "r":
10371                     wrap.setSize(0, b.height);
10372                     wrap.setX(b.right);
10373                     st.left = st.top = "0";
10374                     a = {width: bw, points: pt};
10375                 break;
10376                 case "b":
10377                     wrap.setSize(b.width, 0);
10378                     wrap.setY(b.bottom);
10379                     st.left = st.top = "0";
10380                     a = {height: bh, points: pt};
10381                 break;
10382                 case "tl":
10383                     wrap.setSize(0, 0);
10384                     st.right = st.bottom = "0";
10385                     a = {width: bw, height: bh};
10386                 break;
10387                 case "bl":
10388                     wrap.setSize(0, 0);
10389                     wrap.setY(b.y+b.height);
10390                     st.right = st.top = "0";
10391                     a = {width: bw, height: bh, points: pt};
10392                 break;
10393                 case "br":
10394                     wrap.setSize(0, 0);
10395                     wrap.setXY([b.right, b.bottom]);
10396                     st.left = st.top = "0";
10397                     a = {width: bw, height: bh, points: pt};
10398                 break;
10399                 case "tr":
10400                     wrap.setSize(0, 0);
10401                     wrap.setX(b.x+b.width);
10402                     st.left = st.bottom = "0";
10403                     a = {width: bw, height: bh, points: pt};
10404                 break;
10405             }
10406             this.dom.style.visibility = "visible";
10407             wrap.show();
10408
10409             arguments.callee.anim = wrap.fxanim(a,
10410                 o,
10411                 'motion',
10412                 .5,
10413                 'easeOut', after);
10414         });
10415         return this;
10416     },
10417     
10418         /**
10419          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10420          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10421          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10422          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10423          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10424          * Usage:
10425          *<pre><code>
10426 // default: slide the element out to the top
10427 el.slideOut();
10428
10429 // custom: slide the element out to the right with a 2-second duration
10430 el.slideOut('r', { duration: 2 });
10431
10432 // common config options shown with default values
10433 el.slideOut('t', {
10434     easing: 'easeOut',
10435     duration: .5,
10436     remove: false,
10437     useDisplay: false
10438 });
10439 </code></pre>
10440          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10441          * @param {Object} options (optional) Object literal with any of the Fx config options
10442          * @return {Roo.Element} The Element
10443          */
10444     slideOut : function(anchor, o){
10445         var el = this.getFxEl();
10446         o = o || {};
10447
10448         el.queueFx(o, function(){
10449
10450             anchor = anchor || "t";
10451
10452             // restore values after effect
10453             var r = this.getFxRestore();
10454             
10455             var b = this.getBox();
10456             // fixed size for slide
10457             this.setSize(b);
10458
10459             // wrap if needed
10460             var wrap = this.fxWrap(r.pos, o, "visible");
10461
10462             var st = this.dom.style;
10463             st.visibility = "visible";
10464             st.position = "absolute";
10465
10466             wrap.setSize(b);
10467
10468             var after = function(){
10469                 if(o.useDisplay){
10470                     el.setDisplayed(false);
10471                 }else{
10472                     el.hide();
10473                 }
10474
10475                 el.fxUnwrap(wrap, r.pos, o);
10476
10477                 st.width = r.width;
10478                 st.height = r.height;
10479
10480                 el.afterFx(o);
10481             };
10482
10483             var a, zero = {to: 0};
10484             switch(anchor.toLowerCase()){
10485                 case "t":
10486                     st.left = st.bottom = "0";
10487                     a = {height: zero};
10488                 break;
10489                 case "l":
10490                     st.right = st.top = "0";
10491                     a = {width: zero};
10492                 break;
10493                 case "r":
10494                     st.left = st.top = "0";
10495                     a = {width: zero, points: {to:[b.right, b.y]}};
10496                 break;
10497                 case "b":
10498                     st.left = st.top = "0";
10499                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10500                 break;
10501                 case "tl":
10502                     st.right = st.bottom = "0";
10503                     a = {width: zero, height: zero};
10504                 break;
10505                 case "bl":
10506                     st.right = st.top = "0";
10507                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10508                 break;
10509                 case "br":
10510                     st.left = st.top = "0";
10511                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10512                 break;
10513                 case "tr":
10514                     st.left = st.bottom = "0";
10515                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10516                 break;
10517             }
10518
10519             arguments.callee.anim = wrap.fxanim(a,
10520                 o,
10521                 'motion',
10522                 .5,
10523                 "easeOut", after);
10524         });
10525         return this;
10526     },
10527
10528         /**
10529          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10530          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10531          * The element must be removed from the DOM using the 'remove' config option if desired.
10532          * Usage:
10533          *<pre><code>
10534 // default
10535 el.puff();
10536
10537 // common config options shown with default values
10538 el.puff({
10539     easing: 'easeOut',
10540     duration: .5,
10541     remove: false,
10542     useDisplay: false
10543 });
10544 </code></pre>
10545          * @param {Object} options (optional) Object literal with any of the Fx config options
10546          * @return {Roo.Element} The Element
10547          */
10548     puff : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551
10552         el.queueFx(o, function(){
10553             this.clearOpacity();
10554             this.show();
10555
10556             // restore values after effect
10557             var r = this.getFxRestore();
10558             var st = this.dom.style;
10559
10560             var after = function(){
10561                 if(o.useDisplay){
10562                     el.setDisplayed(false);
10563                 }else{
10564                     el.hide();
10565                 }
10566
10567                 el.clearOpacity();
10568
10569                 el.setPositioning(r.pos);
10570                 st.width = r.width;
10571                 st.height = r.height;
10572                 st.fontSize = '';
10573                 el.afterFx(o);
10574             };
10575
10576             var width = this.getWidth();
10577             var height = this.getHeight();
10578
10579             arguments.callee.anim = this.fxanim({
10580                     width : {to: this.adjustWidth(width * 2)},
10581                     height : {to: this.adjustHeight(height * 2)},
10582                     points : {by: [-(width * .5), -(height * .5)]},
10583                     opacity : {to: 0},
10584                     fontSize: {to:200, unit: "%"}
10585                 },
10586                 o,
10587                 'motion',
10588                 .5,
10589                 "easeOut", after);
10590         });
10591         return this;
10592     },
10593
10594         /**
10595          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10596          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10597          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10598          * Usage:
10599          *<pre><code>
10600 // default
10601 el.switchOff();
10602
10603 // all config options shown with default values
10604 el.switchOff({
10605     easing: 'easeIn',
10606     duration: .3,
10607     remove: false,
10608     useDisplay: false
10609 });
10610 </code></pre>
10611          * @param {Object} options (optional) Object literal with any of the Fx config options
10612          * @return {Roo.Element} The Element
10613          */
10614     switchOff : function(o){
10615         var el = this.getFxEl();
10616         o = o || {};
10617
10618         el.queueFx(o, function(){
10619             this.clearOpacity();
10620             this.clip();
10621
10622             // restore values after effect
10623             var r = this.getFxRestore();
10624             var st = this.dom.style;
10625
10626             var after = function(){
10627                 if(o.useDisplay){
10628                     el.setDisplayed(false);
10629                 }else{
10630                     el.hide();
10631                 }
10632
10633                 el.clearOpacity();
10634                 el.setPositioning(r.pos);
10635                 st.width = r.width;
10636                 st.height = r.height;
10637
10638                 el.afterFx(o);
10639             };
10640
10641             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10642                 this.clearOpacity();
10643                 (function(){
10644                     this.fxanim({
10645                         height:{to:1},
10646                         points:{by:[0, this.getHeight() * .5]}
10647                     }, o, 'motion', 0.3, 'easeIn', after);
10648                 }).defer(100, this);
10649             });
10650         });
10651         return this;
10652     },
10653
10654     /**
10655      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10656      * changed using the "attr" config option) and then fading back to the original color. If no original
10657      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10658      * Usage:
10659 <pre><code>
10660 // default: highlight background to yellow
10661 el.highlight();
10662
10663 // custom: highlight foreground text to blue for 2 seconds
10664 el.highlight("0000ff", { attr: 'color', duration: 2 });
10665
10666 // common config options shown with default values
10667 el.highlight("ffff9c", {
10668     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10669     endColor: (current color) or "ffffff",
10670     easing: 'easeIn',
10671     duration: 1
10672 });
10673 </code></pre>
10674      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10675      * @param {Object} options (optional) Object literal with any of the Fx config options
10676      * @return {Roo.Element} The Element
10677      */ 
10678     highlight : function(color, o){
10679         var el = this.getFxEl();
10680         o = o || {};
10681
10682         el.queueFx(o, function(){
10683             color = color || "ffff9c";
10684             attr = o.attr || "backgroundColor";
10685
10686             this.clearOpacity();
10687             this.show();
10688
10689             var origColor = this.getColor(attr);
10690             var restoreColor = this.dom.style[attr];
10691             endColor = (o.endColor || origColor) || "ffffff";
10692
10693             var after = function(){
10694                 el.dom.style[attr] = restoreColor;
10695                 el.afterFx(o);
10696             };
10697
10698             var a = {};
10699             a[attr] = {from: color, to: endColor};
10700             arguments.callee.anim = this.fxanim(a,
10701                 o,
10702                 'color',
10703                 1,
10704                 'easeIn', after);
10705         });
10706         return this;
10707     },
10708
10709    /**
10710     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10711     * Usage:
10712 <pre><code>
10713 // default: a single light blue ripple
10714 el.frame();
10715
10716 // custom: 3 red ripples lasting 3 seconds total
10717 el.frame("ff0000", 3, { duration: 3 });
10718
10719 // common config options shown with default values
10720 el.frame("C3DAF9", 1, {
10721     duration: 1 //duration of entire animation (not each individual ripple)
10722     // Note: Easing is not configurable and will be ignored if included
10723 });
10724 </code></pre>
10725     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10726     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10727     * @param {Object} options (optional) Object literal with any of the Fx config options
10728     * @return {Roo.Element} The Element
10729     */
10730     frame : function(color, count, o){
10731         var el = this.getFxEl();
10732         o = o || {};
10733
10734         el.queueFx(o, function(){
10735             color = color || "#C3DAF9";
10736             if(color.length == 6){
10737                 color = "#" + color;
10738             }
10739             count = count || 1;
10740             duration = o.duration || 1;
10741             this.show();
10742
10743             var b = this.getBox();
10744             var animFn = function(){
10745                 var proxy = this.createProxy({
10746
10747                      style:{
10748                         visbility:"hidden",
10749                         position:"absolute",
10750                         "z-index":"35000", // yee haw
10751                         border:"0px solid " + color
10752                      }
10753                   });
10754                 var scale = Roo.isBorderBox ? 2 : 1;
10755                 proxy.animate({
10756                     top:{from:b.y, to:b.y - 20},
10757                     left:{from:b.x, to:b.x - 20},
10758                     borderWidth:{from:0, to:10},
10759                     opacity:{from:1, to:0},
10760                     height:{from:b.height, to:(b.height + (20*scale))},
10761                     width:{from:b.width, to:(b.width + (20*scale))}
10762                 }, duration, function(){
10763                     proxy.remove();
10764                 });
10765                 if(--count > 0){
10766                      animFn.defer((duration/2)*1000, this);
10767                 }else{
10768                     el.afterFx(o);
10769                 }
10770             };
10771             animFn.call(this);
10772         });
10773         return this;
10774     },
10775
10776    /**
10777     * Creates a pause before any subsequent queued effects begin.  If there are
10778     * no effects queued after the pause it will have no effect.
10779     * Usage:
10780 <pre><code>
10781 el.pause(1);
10782 </code></pre>
10783     * @param {Number} seconds The length of time to pause (in seconds)
10784     * @return {Roo.Element} The Element
10785     */
10786     pause : function(seconds){
10787         var el = this.getFxEl();
10788         var o = {};
10789
10790         el.queueFx(o, function(){
10791             setTimeout(function(){
10792                 el.afterFx(o);
10793             }, seconds * 1000);
10794         });
10795         return this;
10796     },
10797
10798    /**
10799     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10800     * using the "endOpacity" config option.
10801     * Usage:
10802 <pre><code>
10803 // default: fade in from opacity 0 to 100%
10804 el.fadeIn();
10805
10806 // custom: fade in from opacity 0 to 75% over 2 seconds
10807 el.fadeIn({ endOpacity: .75, duration: 2});
10808
10809 // common config options shown with default values
10810 el.fadeIn({
10811     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10812     easing: 'easeOut',
10813     duration: .5
10814 });
10815 </code></pre>
10816     * @param {Object} options (optional) Object literal with any of the Fx config options
10817     * @return {Roo.Element} The Element
10818     */
10819     fadeIn : function(o){
10820         var el = this.getFxEl();
10821         o = o || {};
10822         el.queueFx(o, function(){
10823             this.setOpacity(0);
10824             this.fixDisplay();
10825             this.dom.style.visibility = 'visible';
10826             var to = o.endOpacity || 1;
10827             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10828                 o, null, .5, "easeOut", function(){
10829                 if(to == 1){
10830                     this.clearOpacity();
10831                 }
10832                 el.afterFx(o);
10833             });
10834         });
10835         return this;
10836     },
10837
10838    /**
10839     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10840     * using the "endOpacity" config option.
10841     * Usage:
10842 <pre><code>
10843 // default: fade out from the element's current opacity to 0
10844 el.fadeOut();
10845
10846 // custom: fade out from the element's current opacity to 25% over 2 seconds
10847 el.fadeOut({ endOpacity: .25, duration: 2});
10848
10849 // common config options shown with default values
10850 el.fadeOut({
10851     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10852     easing: 'easeOut',
10853     duration: .5
10854     remove: false,
10855     useDisplay: false
10856 });
10857 </code></pre>
10858     * @param {Object} options (optional) Object literal with any of the Fx config options
10859     * @return {Roo.Element} The Element
10860     */
10861     fadeOut : function(o){
10862         var el = this.getFxEl();
10863         o = o || {};
10864         el.queueFx(o, function(){
10865             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10866                 o, null, .5, "easeOut", function(){
10867                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10868                      this.dom.style.display = "none";
10869                 }else{
10870                      this.dom.style.visibility = "hidden";
10871                 }
10872                 this.clearOpacity();
10873                 el.afterFx(o);
10874             });
10875         });
10876         return this;
10877     },
10878
10879    /**
10880     * Animates the transition of an element's dimensions from a starting height/width
10881     * to an ending height/width.
10882     * Usage:
10883 <pre><code>
10884 // change height and width to 100x100 pixels
10885 el.scale(100, 100);
10886
10887 // common config options shown with default values.  The height and width will default to
10888 // the element's existing values if passed as null.
10889 el.scale(
10890     [element's width],
10891     [element's height], {
10892     easing: 'easeOut',
10893     duration: .35
10894 });
10895 </code></pre>
10896     * @param {Number} width  The new width (pass undefined to keep the original width)
10897     * @param {Number} height  The new height (pass undefined to keep the original height)
10898     * @param {Object} options (optional) Object literal with any of the Fx config options
10899     * @return {Roo.Element} The Element
10900     */
10901     scale : function(w, h, o){
10902         this.shift(Roo.apply({}, o, {
10903             width: w,
10904             height: h
10905         }));
10906         return this;
10907     },
10908
10909    /**
10910     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10911     * Any of these properties not specified in the config object will not be changed.  This effect 
10912     * requires that at least one new dimension, position or opacity setting must be passed in on
10913     * the config object in order for the function to have any effect.
10914     * Usage:
10915 <pre><code>
10916 // slide the element horizontally to x position 200 while changing the height and opacity
10917 el.shift({ x: 200, height: 50, opacity: .8 });
10918
10919 // common config options shown with default values.
10920 el.shift({
10921     width: [element's width],
10922     height: [element's height],
10923     x: [element's x position],
10924     y: [element's y position],
10925     opacity: [element's opacity],
10926     easing: 'easeOut',
10927     duration: .35
10928 });
10929 </code></pre>
10930     * @param {Object} options  Object literal with any of the Fx config options
10931     * @return {Roo.Element} The Element
10932     */
10933     shift : function(o){
10934         var el = this.getFxEl();
10935         o = o || {};
10936         el.queueFx(o, function(){
10937             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10938             if(w !== undefined){
10939                 a.width = {to: this.adjustWidth(w)};
10940             }
10941             if(h !== undefined){
10942                 a.height = {to: this.adjustHeight(h)};
10943             }
10944             if(x !== undefined || y !== undefined){
10945                 a.points = {to: [
10946                     x !== undefined ? x : this.getX(),
10947                     y !== undefined ? y : this.getY()
10948                 ]};
10949             }
10950             if(op !== undefined){
10951                 a.opacity = {to: op};
10952             }
10953             if(o.xy !== undefined){
10954                 a.points = {to: o.xy};
10955             }
10956             arguments.callee.anim = this.fxanim(a,
10957                 o, 'motion', .35, "easeOut", function(){
10958                 el.afterFx(o);
10959             });
10960         });
10961         return this;
10962     },
10963
10964         /**
10965          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10966          * ending point of the effect.
10967          * Usage:
10968          *<pre><code>
10969 // default: slide the element downward while fading out
10970 el.ghost();
10971
10972 // custom: slide the element out to the right with a 2-second duration
10973 el.ghost('r', { duration: 2 });
10974
10975 // common config options shown with default values
10976 el.ghost('b', {
10977     easing: 'easeOut',
10978     duration: .5
10979     remove: false,
10980     useDisplay: false
10981 });
10982 </code></pre>
10983          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10984          * @param {Object} options (optional) Object literal with any of the Fx config options
10985          * @return {Roo.Element} The Element
10986          */
10987     ghost : function(anchor, o){
10988         var el = this.getFxEl();
10989         o = o || {};
10990
10991         el.queueFx(o, function(){
10992             anchor = anchor || "b";
10993
10994             // restore values after effect
10995             var r = this.getFxRestore();
10996             var w = this.getWidth(),
10997                 h = this.getHeight();
10998
10999             var st = this.dom.style;
11000
11001             var after = function(){
11002                 if(o.useDisplay){
11003                     el.setDisplayed(false);
11004                 }else{
11005                     el.hide();
11006                 }
11007
11008                 el.clearOpacity();
11009                 el.setPositioning(r.pos);
11010                 st.width = r.width;
11011                 st.height = r.height;
11012
11013                 el.afterFx(o);
11014             };
11015
11016             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11017             switch(anchor.toLowerCase()){
11018                 case "t":
11019                     pt.by = [0, -h];
11020                 break;
11021                 case "l":
11022                     pt.by = [-w, 0];
11023                 break;
11024                 case "r":
11025                     pt.by = [w, 0];
11026                 break;
11027                 case "b":
11028                     pt.by = [0, h];
11029                 break;
11030                 case "tl":
11031                     pt.by = [-w, -h];
11032                 break;
11033                 case "bl":
11034                     pt.by = [-w, h];
11035                 break;
11036                 case "br":
11037                     pt.by = [w, h];
11038                 break;
11039                 case "tr":
11040                     pt.by = [w, -h];
11041                 break;
11042             }
11043
11044             arguments.callee.anim = this.fxanim(a,
11045                 o,
11046                 'motion',
11047                 .5,
11048                 "easeOut", after);
11049         });
11050         return this;
11051     },
11052
11053         /**
11054          * Ensures that all effects queued after syncFx is called on the element are
11055          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11056          * @return {Roo.Element} The Element
11057          */
11058     syncFx : function(){
11059         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11060             block : false,
11061             concurrent : true,
11062             stopFx : false
11063         });
11064         return this;
11065     },
11066
11067         /**
11068          * Ensures that all effects queued after sequenceFx is called on the element are
11069          * run in sequence.  This is the opposite of {@link #syncFx}.
11070          * @return {Roo.Element} The Element
11071          */
11072     sequenceFx : function(){
11073         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11074             block : false,
11075             concurrent : false,
11076             stopFx : false
11077         });
11078         return this;
11079     },
11080
11081         /* @private */
11082     nextFx : function(){
11083         var ef = this.fxQueue[0];
11084         if(ef){
11085             ef.call(this);
11086         }
11087     },
11088
11089         /**
11090          * Returns true if the element has any effects actively running or queued, else returns false.
11091          * @return {Boolean} True if element has active effects, else false
11092          */
11093     hasActiveFx : function(){
11094         return this.fxQueue && this.fxQueue[0];
11095     },
11096
11097         /**
11098          * Stops any running effects and clears the element's internal effects queue if it contains
11099          * any additional effects that haven't started yet.
11100          * @return {Roo.Element} The Element
11101          */
11102     stopFx : function(){
11103         if(this.hasActiveFx()){
11104             var cur = this.fxQueue[0];
11105             if(cur && cur.anim && cur.anim.isAnimated()){
11106                 this.fxQueue = [cur]; // clear out others
11107                 cur.anim.stop(true);
11108             }
11109         }
11110         return this;
11111     },
11112
11113         /* @private */
11114     beforeFx : function(o){
11115         if(this.hasActiveFx() && !o.concurrent){
11116            if(o.stopFx){
11117                this.stopFx();
11118                return true;
11119            }
11120            return false;
11121         }
11122         return true;
11123     },
11124
11125         /**
11126          * Returns true if the element is currently blocking so that no other effect can be queued
11127          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11128          * used to ensure that an effect initiated by a user action runs to completion prior to the
11129          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11130          * @return {Boolean} True if blocking, else false
11131          */
11132     hasFxBlock : function(){
11133         var q = this.fxQueue;
11134         return q && q[0] && q[0].block;
11135     },
11136
11137         /* @private */
11138     queueFx : function(o, fn){
11139         if(!this.fxQueue){
11140             this.fxQueue = [];
11141         }
11142         if(!this.hasFxBlock()){
11143             Roo.applyIf(o, this.fxDefaults);
11144             if(!o.concurrent){
11145                 var run = this.beforeFx(o);
11146                 fn.block = o.block;
11147                 this.fxQueue.push(fn);
11148                 if(run){
11149                     this.nextFx();
11150                 }
11151             }else{
11152                 fn.call(this);
11153             }
11154         }
11155         return this;
11156     },
11157
11158         /* @private */
11159     fxWrap : function(pos, o, vis){
11160         var wrap;
11161         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11162             var wrapXY;
11163             if(o.fixPosition){
11164                 wrapXY = this.getXY();
11165             }
11166             var div = document.createElement("div");
11167             div.style.visibility = vis;
11168             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11169             wrap.setPositioning(pos);
11170             if(wrap.getStyle("position") == "static"){
11171                 wrap.position("relative");
11172             }
11173             this.clearPositioning('auto');
11174             wrap.clip();
11175             wrap.dom.appendChild(this.dom);
11176             if(wrapXY){
11177                 wrap.setXY(wrapXY);
11178             }
11179         }
11180         return wrap;
11181     },
11182
11183         /* @private */
11184     fxUnwrap : function(wrap, pos, o){
11185         this.clearPositioning();
11186         this.setPositioning(pos);
11187         if(!o.wrap){
11188             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11189             wrap.remove();
11190         }
11191     },
11192
11193         /* @private */
11194     getFxRestore : function(){
11195         var st = this.dom.style;
11196         return {pos: this.getPositioning(), width: st.width, height : st.height};
11197     },
11198
11199         /* @private */
11200     afterFx : function(o){
11201         if(o.afterStyle){
11202             this.applyStyles(o.afterStyle);
11203         }
11204         if(o.afterCls){
11205             this.addClass(o.afterCls);
11206         }
11207         if(o.remove === true){
11208             this.remove();
11209         }
11210         Roo.callback(o.callback, o.scope, [this]);
11211         if(!o.concurrent){
11212             this.fxQueue.shift();
11213             this.nextFx();
11214         }
11215     },
11216
11217         /* @private */
11218     getFxEl : function(){ // support for composite element fx
11219         return Roo.get(this.dom);
11220     },
11221
11222         /* @private */
11223     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11224         animType = animType || 'run';
11225         opt = opt || {};
11226         var anim = Roo.lib.Anim[animType](
11227             this.dom, args,
11228             (opt.duration || defaultDur) || .35,
11229             (opt.easing || defaultEase) || 'easeOut',
11230             function(){
11231                 Roo.callback(cb, this);
11232             },
11233             this
11234         );
11235         opt.anim = anim;
11236         return anim;
11237     }
11238 };
11239
11240 // backwords compat
11241 Roo.Fx.resize = Roo.Fx.scale;
11242
11243 //When included, Roo.Fx is automatically applied to Element so that all basic
11244 //effects are available directly via the Element API
11245 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11246  * Based on:
11247  * Ext JS Library 1.1.1
11248  * Copyright(c) 2006-2007, Ext JS, LLC.
11249  *
11250  * Originally Released Under LGPL - original licence link has changed is not relivant.
11251  *
11252  * Fork - LGPL
11253  * <script type="text/javascript">
11254  */
11255
11256
11257 /**
11258  * @class Roo.CompositeElement
11259  * Standard composite class. Creates a Roo.Element for every element in the collection.
11260  * <br><br>
11261  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11262  * actions will be performed on all the elements in this collection.</b>
11263  * <br><br>
11264  * All methods return <i>this</i> and can be chained.
11265  <pre><code>
11266  var els = Roo.select("#some-el div.some-class", true);
11267  // or select directly from an existing element
11268  var el = Roo.get('some-el');
11269  el.select('div.some-class', true);
11270
11271  els.setWidth(100); // all elements become 100 width
11272  els.hide(true); // all elements fade out and hide
11273  // or
11274  els.setWidth(100).hide(true);
11275  </code></pre>
11276  */
11277 Roo.CompositeElement = function(els){
11278     this.elements = [];
11279     this.addElements(els);
11280 };
11281 Roo.CompositeElement.prototype = {
11282     isComposite: true,
11283     addElements : function(els){
11284         if(!els) {
11285             return this;
11286         }
11287         if(typeof els == "string"){
11288             els = Roo.Element.selectorFunction(els);
11289         }
11290         var yels = this.elements;
11291         var index = yels.length-1;
11292         for(var i = 0, len = els.length; i < len; i++) {
11293                 yels[++index] = Roo.get(els[i]);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Clears this composite and adds the elements returned by the passed selector.
11300     * @param {String/Array} els A string CSS selector, an array of elements or an element
11301     * @return {CompositeElement} this
11302     */
11303     fill : function(els){
11304         this.elements = [];
11305         this.add(els);
11306         return this;
11307     },
11308
11309     /**
11310     * Filters this composite to only elements that match the passed selector.
11311     * @param {String} selector A string CSS selector
11312     * @param {Boolean} inverse return inverse filter (not matches)
11313     * @return {CompositeElement} this
11314     */
11315     filter : function(selector, inverse){
11316         var els = [];
11317         inverse = inverse || false;
11318         this.each(function(el){
11319             var match = inverse ? !el.is(selector) : el.is(selector);
11320             if(match){
11321                 els[els.length] = el.dom;
11322             }
11323         });
11324         this.fill(els);
11325         return this;
11326     },
11327
11328     invoke : function(fn, args){
11329         var els = this.elements;
11330         for(var i = 0, len = els.length; i < len; i++) {
11331                 Roo.Element.prototype[fn].apply(els[i], args);
11332         }
11333         return this;
11334     },
11335     /**
11336     * Adds elements to this composite.
11337     * @param {String/Array} els A string CSS selector, an array of elements or an element
11338     * @return {CompositeElement} this
11339     */
11340     add : function(els){
11341         if(typeof els == "string"){
11342             this.addElements(Roo.Element.selectorFunction(els));
11343         }else if(els.length !== undefined){
11344             this.addElements(els);
11345         }else{
11346             this.addElements([els]);
11347         }
11348         return this;
11349     },
11350     /**
11351     * Calls the passed function passing (el, this, index) for each element in this composite.
11352     * @param {Function} fn The function to call
11353     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11354     * @return {CompositeElement} this
11355     */
11356     each : function(fn, scope){
11357         var els = this.elements;
11358         for(var i = 0, len = els.length; i < len; i++){
11359             if(fn.call(scope || els[i], els[i], this, i) === false) {
11360                 break;
11361             }
11362         }
11363         return this;
11364     },
11365
11366     /**
11367      * Returns the Element object at the specified index
11368      * @param {Number} index
11369      * @return {Roo.Element}
11370      */
11371     item : function(index){
11372         return this.elements[index] || null;
11373     },
11374
11375     /**
11376      * Returns the first Element
11377      * @return {Roo.Element}
11378      */
11379     first : function(){
11380         return this.item(0);
11381     },
11382
11383     /**
11384      * Returns the last Element
11385      * @return {Roo.Element}
11386      */
11387     last : function(){
11388         return this.item(this.elements.length-1);
11389     },
11390
11391     /**
11392      * Returns the number of elements in this composite
11393      * @return Number
11394      */
11395     getCount : function(){
11396         return this.elements.length;
11397     },
11398
11399     /**
11400      * Returns true if this composite contains the passed element
11401      * @return Boolean
11402      */
11403     contains : function(el){
11404         return this.indexOf(el) !== -1;
11405     },
11406
11407     /**
11408      * Returns true if this composite contains the passed element
11409      * @return Boolean
11410      */
11411     indexOf : function(el){
11412         return this.elements.indexOf(Roo.get(el));
11413     },
11414
11415
11416     /**
11417     * Removes the specified element(s).
11418     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11419     * or an array of any of those.
11420     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11421     * @return {CompositeElement} this
11422     */
11423     removeElement : function(el, removeDom){
11424         if(el instanceof Array){
11425             for(var i = 0, len = el.length; i < len; i++){
11426                 this.removeElement(el[i]);
11427             }
11428             return this;
11429         }
11430         var index = typeof el == 'number' ? el : this.indexOf(el);
11431         if(index !== -1){
11432             if(removeDom){
11433                 var d = this.elements[index];
11434                 if(d.dom){
11435                     d.remove();
11436                 }else{
11437                     d.parentNode.removeChild(d);
11438                 }
11439             }
11440             this.elements.splice(index, 1);
11441         }
11442         return this;
11443     },
11444
11445     /**
11446     * Replaces the specified element with the passed element.
11447     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11448     * to replace.
11449     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11450     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11451     * @return {CompositeElement} this
11452     */
11453     replaceElement : function(el, replacement, domReplace){
11454         var index = typeof el == 'number' ? el : this.indexOf(el);
11455         if(index !== -1){
11456             if(domReplace){
11457                 this.elements[index].replaceWith(replacement);
11458             }else{
11459                 this.elements.splice(index, 1, Roo.get(replacement))
11460             }
11461         }
11462         return this;
11463     },
11464
11465     /**
11466      * Removes all elements.
11467      */
11468     clear : function(){
11469         this.elements = [];
11470     }
11471 };
11472 (function(){
11473     Roo.CompositeElement.createCall = function(proto, fnName){
11474         if(!proto[fnName]){
11475             proto[fnName] = function(){
11476                 return this.invoke(fnName, arguments);
11477             };
11478         }
11479     };
11480     for(var fnName in Roo.Element.prototype){
11481         if(typeof Roo.Element.prototype[fnName] == "function"){
11482             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11483         }
11484     };
11485 })();
11486 /*
11487  * Based on:
11488  * Ext JS Library 1.1.1
11489  * Copyright(c) 2006-2007, Ext JS, LLC.
11490  *
11491  * Originally Released Under LGPL - original licence link has changed is not relivant.
11492  *
11493  * Fork - LGPL
11494  * <script type="text/javascript">
11495  */
11496
11497 /**
11498  * @class Roo.CompositeElementLite
11499  * @extends Roo.CompositeElement
11500  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11501  <pre><code>
11502  var els = Roo.select("#some-el div.some-class");
11503  // or select directly from an existing element
11504  var el = Roo.get('some-el');
11505  el.select('div.some-class');
11506
11507  els.setWidth(100); // all elements become 100 width
11508  els.hide(true); // all elements fade out and hide
11509  // or
11510  els.setWidth(100).hide(true);
11511  </code></pre><br><br>
11512  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11513  * actions will be performed on all the elements in this collection.</b>
11514  */
11515 Roo.CompositeElementLite = function(els){
11516     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11517     this.el = new Roo.Element.Flyweight();
11518 };
11519 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11520     addElements : function(els){
11521         if(els){
11522             if(els instanceof Array){
11523                 this.elements = this.elements.concat(els);
11524             }else{
11525                 var yels = this.elements;
11526                 var index = yels.length-1;
11527                 for(var i = 0, len = els.length; i < len; i++) {
11528                     yels[++index] = els[i];
11529                 }
11530             }
11531         }
11532         return this;
11533     },
11534     invoke : function(fn, args){
11535         var els = this.elements;
11536         var el = this.el;
11537         for(var i = 0, len = els.length; i < len; i++) {
11538             el.dom = els[i];
11539                 Roo.Element.prototype[fn].apply(el, args);
11540         }
11541         return this;
11542     },
11543     /**
11544      * Returns a flyweight Element of the dom element object at the specified index
11545      * @param {Number} index
11546      * @return {Roo.Element}
11547      */
11548     item : function(index){
11549         if(!this.elements[index]){
11550             return null;
11551         }
11552         this.el.dom = this.elements[index];
11553         return this.el;
11554     },
11555
11556     // fixes scope with flyweight
11557     addListener : function(eventName, handler, scope, opt){
11558         var els = this.elements;
11559         for(var i = 0, len = els.length; i < len; i++) {
11560             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11561         }
11562         return this;
11563     },
11564
11565     /**
11566     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11567     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11568     * a reference to the dom node, use el.dom.</b>
11569     * @param {Function} fn The function to call
11570     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11571     * @return {CompositeElement} this
11572     */
11573     each : function(fn, scope){
11574         var els = this.elements;
11575         var el = this.el;
11576         for(var i = 0, len = els.length; i < len; i++){
11577             el.dom = els[i];
11578                 if(fn.call(scope || el, el, this, i) === false){
11579                 break;
11580             }
11581         }
11582         return this;
11583     },
11584
11585     indexOf : function(el){
11586         return this.elements.indexOf(Roo.getDom(el));
11587     },
11588
11589     replaceElement : function(el, replacement, domReplace){
11590         var index = typeof el == 'number' ? el : this.indexOf(el);
11591         if(index !== -1){
11592             replacement = Roo.getDom(replacement);
11593             if(domReplace){
11594                 var d = this.elements[index];
11595                 d.parentNode.insertBefore(replacement, d);
11596                 d.parentNode.removeChild(d);
11597             }
11598             this.elements.splice(index, 1, replacement);
11599         }
11600         return this;
11601     }
11602 });
11603 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11604
11605 /*
11606  * Based on:
11607  * Ext JS Library 1.1.1
11608  * Copyright(c) 2006-2007, Ext JS, LLC.
11609  *
11610  * Originally Released Under LGPL - original licence link has changed is not relivant.
11611  *
11612  * Fork - LGPL
11613  * <script type="text/javascript">
11614  */
11615
11616  
11617
11618 /**
11619  * @class Roo.data.Connection
11620  * @extends Roo.util.Observable
11621  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11622  * either to a configured URL, or to a URL specified at request time. 
11623  * 
11624  * Requests made by this class are asynchronous, and will return immediately. No data from
11625  * the server will be available to the statement immediately following the {@link #request} call.
11626  * To process returned data, use a callback in the request options object, or an event listener.
11627  * 
11628  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11629  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11630  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11631  * property and, if present, the IFRAME's XML document as the responseXML property.
11632  * 
11633  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11634  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11635  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11636  * standard DOM methods.
11637  * @constructor
11638  * @param {Object} config a configuration object.
11639  */
11640 Roo.data.Connection = function(config){
11641     Roo.apply(this, config);
11642     this.addEvents({
11643         /**
11644          * @event beforerequest
11645          * Fires before a network request is made to retrieve a data object.
11646          * @param {Connection} conn This Connection object.
11647          * @param {Object} options The options config object passed to the {@link #request} method.
11648          */
11649         "beforerequest" : true,
11650         /**
11651          * @event requestcomplete
11652          * Fires if the request was successfully completed.
11653          * @param {Connection} conn This Connection object.
11654          * @param {Object} response The XHR object containing the response data.
11655          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11656          * @param {Object} options The options config object passed to the {@link #request} method.
11657          */
11658         "requestcomplete" : true,
11659         /**
11660          * @event requestexception
11661          * Fires if an error HTTP status was returned from the server.
11662          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11663          * @param {Connection} conn This Connection object.
11664          * @param {Object} response The XHR object containing the response data.
11665          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11666          * @param {Object} options The options config object passed to the {@link #request} method.
11667          */
11668         "requestexception" : true
11669     });
11670     Roo.data.Connection.superclass.constructor.call(this);
11671 };
11672
11673 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11674     /**
11675      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11676      */
11677     /**
11678      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11679      * extra parameters to each request made by this object. (defaults to undefined)
11680      */
11681     /**
11682      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11683      *  to each request made by this object. (defaults to undefined)
11684      */
11685     /**
11686      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11687      */
11688     /**
11689      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11690      */
11691     timeout : 30000,
11692     /**
11693      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11694      * @type Boolean
11695      */
11696     autoAbort:false,
11697
11698     /**
11699      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11700      * @type Boolean
11701      */
11702     disableCaching: true,
11703
11704     /**
11705      * Sends an HTTP request to a remote server.
11706      * @param {Object} options An object which may contain the following properties:<ul>
11707      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11708      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11709      * request, a url encoded string or a function to call to get either.</li>
11710      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11711      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11712      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11713      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11714      * <li>options {Object} The parameter to the request call.</li>
11715      * <li>success {Boolean} True if the request succeeded.</li>
11716      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11717      * </ul></li>
11718      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11719      * The callback is passed the following parameters:<ul>
11720      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11721      * <li>options {Object} The parameter to the request call.</li>
11722      * </ul></li>
11723      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11724      * The callback is passed the following parameters:<ul>
11725      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11726      * <li>options {Object} The parameter to the request call.</li>
11727      * </ul></li>
11728      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11729      * for the callback function. Defaults to the browser window.</li>
11730      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11731      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11732      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11733      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11734      * params for the post data. Any params will be appended to the URL.</li>
11735      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11736      * </ul>
11737      * @return {Number} transactionId
11738      */
11739     request : function(o){
11740         if(this.fireEvent("beforerequest", this, o) !== false){
11741             var p = o.params;
11742
11743             if(typeof p == "function"){
11744                 p = p.call(o.scope||window, o);
11745             }
11746             if(typeof p == "object"){
11747                 p = Roo.urlEncode(o.params);
11748             }
11749             if(this.extraParams){
11750                 var extras = Roo.urlEncode(this.extraParams);
11751                 p = p ? (p + '&' + extras) : extras;
11752             }
11753
11754             var url = o.url || this.url;
11755             if(typeof url == 'function'){
11756                 url = url.call(o.scope||window, o);
11757             }
11758
11759             if(o.form){
11760                 var form = Roo.getDom(o.form);
11761                 url = url || form.action;
11762
11763                 var enctype = form.getAttribute("enctype");
11764                 
11765                 if (o.formData) {
11766                     return this.doFormDataUpload(o, url);
11767                 }
11768                 
11769                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11770                     return this.doFormUpload(o, p, url);
11771                 }
11772                 var f = Roo.lib.Ajax.serializeForm(form);
11773                 p = p ? (p + '&' + f) : f;
11774             }
11775             
11776             if (!o.form && o.formData) {
11777                 o.formData = o.formData === true ? new FormData() : o.formData;
11778                 for (var k in o.params) {
11779                     o.formData.append(k,o.params[k]);
11780                 }
11781                     
11782                 return this.doFormDataUpload(o, url);
11783             }
11784             
11785
11786             var hs = o.headers;
11787             if(this.defaultHeaders){
11788                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11789                 if(!o.headers){
11790                     o.headers = hs;
11791                 }
11792             }
11793
11794             var cb = {
11795                 success: this.handleResponse,
11796                 failure: this.handleFailure,
11797                 scope: this,
11798                 argument: {options: o},
11799                 timeout : o.timeout || this.timeout
11800             };
11801
11802             var method = o.method||this.method||(p ? "POST" : "GET");
11803
11804             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11805                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11806             }
11807
11808             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11809                 if(o.autoAbort){
11810                     this.abort();
11811                 }
11812             }else if(this.autoAbort !== false){
11813                 this.abort();
11814             }
11815
11816             if((method == 'GET' && p) || o.xmlData){
11817                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11818                 p = '';
11819             }
11820             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11821             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11822             Roo.lib.Ajax.useDefaultHeader == true;
11823             return this.transId;
11824         }else{
11825             Roo.callback(o.callback, o.scope, [o, null, null]);
11826             return null;
11827         }
11828     },
11829
11830     /**
11831      * Determine whether this object has a request outstanding.
11832      * @param {Number} transactionId (Optional) defaults to the last transaction
11833      * @return {Boolean} True if there is an outstanding request.
11834      */
11835     isLoading : function(transId){
11836         if(transId){
11837             return Roo.lib.Ajax.isCallInProgress(transId);
11838         }else{
11839             return this.transId ? true : false;
11840         }
11841     },
11842
11843     /**
11844      * Aborts any outstanding request.
11845      * @param {Number} transactionId (Optional) defaults to the last transaction
11846      */
11847     abort : function(transId){
11848         if(transId || this.isLoading()){
11849             Roo.lib.Ajax.abort(transId || this.transId);
11850         }
11851     },
11852
11853     // private
11854     handleResponse : function(response){
11855         this.transId = false;
11856         var options = response.argument.options;
11857         response.argument = options ? options.argument : null;
11858         this.fireEvent("requestcomplete", this, response, options);
11859         Roo.callback(options.success, options.scope, [response, options]);
11860         Roo.callback(options.callback, options.scope, [options, true, response]);
11861     },
11862
11863     // private
11864     handleFailure : function(response, e){
11865         this.transId = false;
11866         var options = response.argument.options;
11867         response.argument = options ? options.argument : null;
11868         this.fireEvent("requestexception", this, response, options, e);
11869         Roo.callback(options.failure, options.scope, [response, options]);
11870         Roo.callback(options.callback, options.scope, [options, false, response]);
11871     },
11872
11873     // private
11874     doFormUpload : function(o, ps, url){
11875         var id = Roo.id();
11876         var frame = document.createElement('iframe');
11877         frame.id = id;
11878         frame.name = id;
11879         frame.className = 'x-hidden';
11880         if(Roo.isIE){
11881             frame.src = Roo.SSL_SECURE_URL;
11882         }
11883         document.body.appendChild(frame);
11884
11885         if(Roo.isIE){
11886            document.frames[id].name = id;
11887         }
11888
11889         var form = Roo.getDom(o.form);
11890         form.target = id;
11891         form.method = 'POST';
11892         form.enctype = form.encoding = 'multipart/form-data';
11893         if(url){
11894             form.action = url;
11895         }
11896
11897         var hiddens, hd;
11898         if(ps){ // add dynamic params
11899             hiddens = [];
11900             ps = Roo.urlDecode(ps, false);
11901             for(var k in ps){
11902                 if(ps.hasOwnProperty(k)){
11903                     hd = document.createElement('input');
11904                     hd.type = 'hidden';
11905                     hd.name = k;
11906                     hd.value = ps[k];
11907                     form.appendChild(hd);
11908                     hiddens.push(hd);
11909                 }
11910             }
11911         }
11912
11913         function cb(){
11914             var r = {  // bogus response object
11915                 responseText : '',
11916                 responseXML : null
11917             };
11918
11919             r.argument = o ? o.argument : null;
11920
11921             try { //
11922                 var doc;
11923                 if(Roo.isIE){
11924                     doc = frame.contentWindow.document;
11925                 }else {
11926                     doc = (frame.contentDocument || window.frames[id].document);
11927                 }
11928                 if(doc && doc.body){
11929                     r.responseText = doc.body.innerHTML;
11930                 }
11931                 if(doc && doc.XMLDocument){
11932                     r.responseXML = doc.XMLDocument;
11933                 }else {
11934                     r.responseXML = doc;
11935                 }
11936             }
11937             catch(e) {
11938                 // ignore
11939             }
11940
11941             Roo.EventManager.removeListener(frame, 'load', cb, this);
11942
11943             this.fireEvent("requestcomplete", this, r, o);
11944             Roo.callback(o.success, o.scope, [r, o]);
11945             Roo.callback(o.callback, o.scope, [o, true, r]);
11946
11947             setTimeout(function(){document.body.removeChild(frame);}, 100);
11948         }
11949
11950         Roo.EventManager.on(frame, 'load', cb, this);
11951         form.submit();
11952
11953         if(hiddens){ // remove dynamic params
11954             for(var i = 0, len = hiddens.length; i < len; i++){
11955                 form.removeChild(hiddens[i]);
11956             }
11957         }
11958     },
11959     // this is a 'formdata version???'
11960     
11961     
11962     doFormDataUpload : function(o,  url)
11963     {
11964         var formData;
11965         if (o.form) {
11966             var form =  Roo.getDom(o.form);
11967             form.enctype = form.encoding = 'multipart/form-data';
11968             formData = o.formData === true ? new FormData(form) : o.formData;
11969         } else {
11970             formData = o.formData === true ? new FormData() : o.formData;
11971         }
11972         
11973       
11974         var cb = {
11975             success: this.handleResponse,
11976             failure: this.handleFailure,
11977             scope: this,
11978             argument: {options: o},
11979             timeout : o.timeout || this.timeout
11980         };
11981  
11982         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11983             if(o.autoAbort){
11984                 this.abort();
11985             }
11986         }else if(this.autoAbort !== false){
11987             this.abort();
11988         }
11989
11990         //Roo.lib.Ajax.defaultPostHeader = null;
11991         Roo.lib.Ajax.useDefaultHeader = false;
11992         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11993         Roo.lib.Ajax.useDefaultHeader = true;
11994  
11995          
11996     }
11997     
11998 });
11999 /*
12000  * Based on:
12001  * Ext JS Library 1.1.1
12002  * Copyright(c) 2006-2007, Ext JS, LLC.
12003  *
12004  * Originally Released Under LGPL - original licence link has changed is not relivant.
12005  *
12006  * Fork - LGPL
12007  * <script type="text/javascript">
12008  */
12009  
12010 /**
12011  * Global Ajax request class.
12012  * 
12013  * @class Roo.Ajax
12014  * @extends Roo.data.Connection
12015  * @static
12016  * 
12017  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12018  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12019  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12020  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12021  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12022  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12023  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12024  */
12025 Roo.Ajax = new Roo.data.Connection({
12026     // fix up the docs
12027     /**
12028      * @scope Roo.Ajax
12029      * @type {Boolear} 
12030      */
12031     autoAbort : false,
12032
12033     /**
12034      * Serialize the passed form into a url encoded string
12035      * @scope Roo.Ajax
12036      * @param {String/HTMLElement} form
12037      * @return {String}
12038      */
12039     serializeForm : function(form){
12040         return Roo.lib.Ajax.serializeForm(form);
12041     }
12042 });/*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053  
12054 /**
12055  * @class Roo.UpdateManager
12056  * @extends Roo.util.Observable
12057  * Provides AJAX-style update for Element object.<br><br>
12058  * Usage:<br>
12059  * <pre><code>
12060  * // Get it from a Roo.Element object
12061  * var el = Roo.get("foo");
12062  * var mgr = el.getUpdateManager();
12063  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12064  * ...
12065  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12066  * <br>
12067  * // or directly (returns the same UpdateManager instance)
12068  * var mgr = new Roo.UpdateManager("myElementId");
12069  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12070  * mgr.on("update", myFcnNeedsToKnow);
12071  * <br>
12072    // short handed call directly from the element object
12073    Roo.get("foo").load({
12074         url: "bar.php",
12075         scripts:true,
12076         params: "for=bar",
12077         text: "Loading Foo..."
12078    });
12079  * </code></pre>
12080  * @constructor
12081  * Create new UpdateManager directly.
12082  * @param {String/HTMLElement/Roo.Element} el The element to update
12083  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
12084  */
12085 Roo.UpdateManager = function(el, forceNew){
12086     el = Roo.get(el);
12087     if(!forceNew && el.updateManager){
12088         return el.updateManager;
12089     }
12090     /**
12091      * The Element object
12092      * @type Roo.Element
12093      */
12094     this.el = el;
12095     /**
12096      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12097      * @type String
12098      */
12099     this.defaultUrl = null;
12100
12101     this.addEvents({
12102         /**
12103          * @event beforeupdate
12104          * Fired before an update is made, return false from your handler and the update is cancelled.
12105          * @param {Roo.Element} el
12106          * @param {String/Object/Function} url
12107          * @param {String/Object} params
12108          */
12109         "beforeupdate": true,
12110         /**
12111          * @event update
12112          * Fired after successful update is made.
12113          * @param {Roo.Element} el
12114          * @param {Object} oResponseObject The response Object
12115          */
12116         "update": true,
12117         /**
12118          * @event failure
12119          * Fired on update failure.
12120          * @param {Roo.Element} el
12121          * @param {Object} oResponseObject The response Object
12122          */
12123         "failure": true
12124     });
12125     var d = Roo.UpdateManager.defaults;
12126     /**
12127      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12128      * @type String
12129      */
12130     this.sslBlankUrl = d.sslBlankUrl;
12131     /**
12132      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12133      * @type Boolean
12134      */
12135     this.disableCaching = d.disableCaching;
12136     /**
12137      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12138      * @type String
12139      */
12140     this.indicatorText = d.indicatorText;
12141     /**
12142      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12143      * @type String
12144      */
12145     this.showLoadIndicator = d.showLoadIndicator;
12146     /**
12147      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12148      * @type Number
12149      */
12150     this.timeout = d.timeout;
12151
12152     /**
12153      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12154      * @type Boolean
12155      */
12156     this.loadScripts = d.loadScripts;
12157
12158     /**
12159      * Transaction object of current executing transaction
12160      */
12161     this.transaction = null;
12162
12163     /**
12164      * @private
12165      */
12166     this.autoRefreshProcId = null;
12167     /**
12168      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12169      * @type Function
12170      */
12171     this.refreshDelegate = this.refresh.createDelegate(this);
12172     /**
12173      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12174      * @type Function
12175      */
12176     this.updateDelegate = this.update.createDelegate(this);
12177     /**
12178      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12179      * @type Function
12180      */
12181     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12182     /**
12183      * @private
12184      */
12185     this.successDelegate = this.processSuccess.createDelegate(this);
12186     /**
12187      * @private
12188      */
12189     this.failureDelegate = this.processFailure.createDelegate(this);
12190
12191     if(!this.renderer){
12192      /**
12193       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12194       */
12195     this.renderer = new Roo.UpdateManager.BasicRenderer();
12196     }
12197     
12198     Roo.UpdateManager.superclass.constructor.call(this);
12199 };
12200
12201 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12202     /**
12203      * Get the Element this UpdateManager is bound to
12204      * @return {Roo.Element} The element
12205      */
12206     getEl : function(){
12207         return this.el;
12208     },
12209     /**
12210      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12211      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12212 <pre><code>
12213 um.update({<br/>
12214     url: "your-url.php",<br/>
12215     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12216     callback: yourFunction,<br/>
12217     scope: yourObject, //(optional scope)  <br/>
12218     discardUrl: false, <br/>
12219     nocache: false,<br/>
12220     text: "Loading...",<br/>
12221     timeout: 30,<br/>
12222     scripts: false<br/>
12223 });
12224 </code></pre>
12225      * The only required property is url. The optional properties nocache, text and scripts
12226      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12227      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12228      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12229      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12230      */
12231     update : function(url, params, callback, discardUrl){
12232         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12233             var method = this.method,
12234                 cfg;
12235             if(typeof url == "object"){ // must be config object
12236                 cfg = url;
12237                 url = cfg.url;
12238                 params = params || cfg.params;
12239                 callback = callback || cfg.callback;
12240                 discardUrl = discardUrl || cfg.discardUrl;
12241                 if(callback && cfg.scope){
12242                     callback = callback.createDelegate(cfg.scope);
12243                 }
12244                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12245                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12246                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12247                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12248                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12249             }
12250             this.showLoading();
12251             if(!discardUrl){
12252                 this.defaultUrl = url;
12253             }
12254             if(typeof url == "function"){
12255                 url = url.call(this);
12256             }
12257
12258             method = method || (params ? "POST" : "GET");
12259             if(method == "GET"){
12260                 url = this.prepareUrl(url);
12261             }
12262
12263             var o = Roo.apply(cfg ||{}, {
12264                 url : url,
12265                 params: params,
12266                 success: this.successDelegate,
12267                 failure: this.failureDelegate,
12268                 callback: undefined,
12269                 timeout: (this.timeout*1000),
12270                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12271             });
12272             Roo.log("updated manager called with timeout of " + o.timeout);
12273             this.transaction = Roo.Ajax.request(o);
12274         }
12275     },
12276
12277     /**
12278      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12279      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12280      * @param {String/HTMLElement} form The form Id or form element
12281      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12282      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12284      */
12285     formUpdate : function(form, url, reset, callback){
12286         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12287             if(typeof url == "function"){
12288                 url = url.call(this);
12289             }
12290             form = Roo.getDom(form);
12291             this.transaction = Roo.Ajax.request({
12292                 form: form,
12293                 url:url,
12294                 success: this.successDelegate,
12295                 failure: this.failureDelegate,
12296                 timeout: (this.timeout*1000),
12297                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12298             });
12299             this.showLoading.defer(1, this);
12300         }
12301     },
12302
12303     /**
12304      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12305      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12306      */
12307     refresh : function(callback){
12308         if(this.defaultUrl == null){
12309             return;
12310         }
12311         this.update(this.defaultUrl, null, callback, true);
12312     },
12313
12314     /**
12315      * Set this element to auto refresh.
12316      * @param {Number} interval How often to update (in seconds).
12317      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12318      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12319      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12320      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12321      */
12322     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12323         if(refreshNow){
12324             this.update(url || this.defaultUrl, params, callback, true);
12325         }
12326         if(this.autoRefreshProcId){
12327             clearInterval(this.autoRefreshProcId);
12328         }
12329         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12330     },
12331
12332     /**
12333      * Stop auto refresh on this element.
12334      */
12335      stopAutoRefresh : function(){
12336         if(this.autoRefreshProcId){
12337             clearInterval(this.autoRefreshProcId);
12338             delete this.autoRefreshProcId;
12339         }
12340     },
12341
12342     isAutoRefreshing : function(){
12343        return this.autoRefreshProcId ? true : false;
12344     },
12345     /**
12346      * Called to update the element to "Loading" state. Override to perform custom action.
12347      */
12348     showLoading : function(){
12349         if(this.showLoadIndicator){
12350             this.el.update(this.indicatorText);
12351         }
12352     },
12353
12354     /**
12355      * Adds unique parameter to query string if disableCaching = true
12356      * @private
12357      */
12358     prepareUrl : function(url){
12359         if(this.disableCaching){
12360             var append = "_dc=" + (new Date().getTime());
12361             if(url.indexOf("?") !== -1){
12362                 url += "&" + append;
12363             }else{
12364                 url += "?" + append;
12365             }
12366         }
12367         return url;
12368     },
12369
12370     /**
12371      * @private
12372      */
12373     processSuccess : function(response){
12374         this.transaction = null;
12375         if(response.argument.form && response.argument.reset){
12376             try{ // put in try/catch since some older FF releases had problems with this
12377                 response.argument.form.reset();
12378             }catch(e){}
12379         }
12380         if(this.loadScripts){
12381             this.renderer.render(this.el, response, this,
12382                 this.updateComplete.createDelegate(this, [response]));
12383         }else{
12384             this.renderer.render(this.el, response, this);
12385             this.updateComplete(response);
12386         }
12387     },
12388
12389     updateComplete : function(response){
12390         this.fireEvent("update", this.el, response);
12391         if(typeof response.argument.callback == "function"){
12392             response.argument.callback(this.el, true, response);
12393         }
12394     },
12395
12396     /**
12397      * @private
12398      */
12399     processFailure : function(response){
12400         this.transaction = null;
12401         this.fireEvent("failure", this.el, response);
12402         if(typeof response.argument.callback == "function"){
12403             response.argument.callback(this.el, false, response);
12404         }
12405     },
12406
12407     /**
12408      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12409      * @param {Object} renderer The object implementing the render() method
12410      */
12411     setRenderer : function(renderer){
12412         this.renderer = renderer;
12413     },
12414
12415     getRenderer : function(){
12416        return this.renderer;
12417     },
12418
12419     /**
12420      * Set the defaultUrl used for updates
12421      * @param {String/Function} defaultUrl The url or a function to call to get the url
12422      */
12423     setDefaultUrl : function(defaultUrl){
12424         this.defaultUrl = defaultUrl;
12425     },
12426
12427     /**
12428      * Aborts the executing transaction
12429      */
12430     abort : function(){
12431         if(this.transaction){
12432             Roo.Ajax.abort(this.transaction);
12433         }
12434     },
12435
12436     /**
12437      * Returns true if an update is in progress
12438      * @return {Boolean}
12439      */
12440     isUpdating : function(){
12441         if(this.transaction){
12442             return Roo.Ajax.isLoading(this.transaction);
12443         }
12444         return false;
12445     }
12446 });
12447
12448 /**
12449  * @class Roo.UpdateManager.defaults
12450  * @static (not really - but it helps the doc tool)
12451  * The defaults collection enables customizing the default properties of UpdateManager
12452  */
12453    Roo.UpdateManager.defaults = {
12454        /**
12455          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12456          * @type Number
12457          */
12458          timeout : 30,
12459
12460          /**
12461          * True to process scripts by default (Defaults to false).
12462          * @type Boolean
12463          */
12464         loadScripts : false,
12465
12466         /**
12467         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12468         * @type String
12469         */
12470         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12471         /**
12472          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12473          * @type Boolean
12474          */
12475         disableCaching : false,
12476         /**
12477          * Whether to show indicatorText when loading (Defaults to true).
12478          * @type Boolean
12479          */
12480         showLoadIndicator : true,
12481         /**
12482          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12483          * @type String
12484          */
12485         indicatorText : '<div class="loading-indicator">Loading...</div>'
12486    };
12487
12488 /**
12489  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12490  *Usage:
12491  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12492  * @param {String/HTMLElement/Roo.Element} el The element to update
12493  * @param {String} url The url
12494  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12495  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12496  * @static
12497  * @deprecated
12498  * @member Roo.UpdateManager
12499  */
12500 Roo.UpdateManager.updateElement = function(el, url, params, options){
12501     var um = Roo.get(el, true).getUpdateManager();
12502     Roo.apply(um, options);
12503     um.update(url, params, options ? options.callback : null);
12504 };
12505 // alias for backwards compat
12506 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12507 /**
12508  * @class Roo.UpdateManager.BasicRenderer
12509  * Default Content renderer. Updates the elements innerHTML with the responseText.
12510  */
12511 Roo.UpdateManager.BasicRenderer = function(){};
12512
12513 Roo.UpdateManager.BasicRenderer.prototype = {
12514     /**
12515      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12516      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12517      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12518      * @param {Roo.Element} el The element being rendered
12519      * @param {Object} response The YUI Connect response object
12520      * @param {UpdateManager} updateManager The calling update manager
12521      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12522      */
12523      render : function(el, response, updateManager, callback){
12524         el.update(response.responseText, updateManager.loadScripts, callback);
12525     }
12526 };
12527 /*
12528  * Based on:
12529  * Roo JS
12530  * (c)) Alan Knowles
12531  * Licence : LGPL
12532  */
12533
12534
12535 /**
12536  * @class Roo.DomTemplate
12537  * @extends Roo.Template
12538  * An effort at a dom based template engine..
12539  *
12540  * Similar to XTemplate, except it uses dom parsing to create the template..
12541  *
12542  * Supported features:
12543  *
12544  *  Tags:
12545
12546 <pre><code>
12547       {a_variable} - output encoded.
12548       {a_variable.format:("Y-m-d")} - call a method on the variable
12549       {a_variable:raw} - unencoded output
12550       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12551       {a_variable:this.method_on_template(...)} - call a method on the template object.
12552  
12553 </code></pre>
12554  *  The tpl tag:
12555 <pre><code>
12556         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12557         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12558         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12559         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12560   
12561 </code></pre>
12562  *      
12563  */
12564 Roo.DomTemplate = function()
12565 {
12566      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12567      if (this.html) {
12568         this.compile();
12569      }
12570 };
12571
12572
12573 Roo.extend(Roo.DomTemplate, Roo.Template, {
12574     /**
12575      * id counter for sub templates.
12576      */
12577     id : 0,
12578     /**
12579      * flag to indicate if dom parser is inside a pre,
12580      * it will strip whitespace if not.
12581      */
12582     inPre : false,
12583     
12584     /**
12585      * The various sub templates
12586      */
12587     tpls : false,
12588     
12589     
12590     
12591     /**
12592      *
12593      * basic tag replacing syntax
12594      * WORD:WORD()
12595      *
12596      * // you can fake an object call by doing this
12597      *  x.t:(test,tesT) 
12598      * 
12599      */
12600     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12601     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12602     
12603     iterChild : function (node, method) {
12604         
12605         var oldPre = this.inPre;
12606         if (node.tagName == 'PRE') {
12607             this.inPre = true;
12608         }
12609         for( var i = 0; i < node.childNodes.length; i++) {
12610             method.call(this, node.childNodes[i]);
12611         }
12612         this.inPre = oldPre;
12613     },
12614     
12615     
12616     
12617     /**
12618      * compile the template
12619      *
12620      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12621      *
12622      */
12623     compile: function()
12624     {
12625         var s = this.html;
12626         
12627         // covert the html into DOM...
12628         var doc = false;
12629         var div =false;
12630         try {
12631             doc = document.implementation.createHTMLDocument("");
12632             doc.documentElement.innerHTML =   this.html  ;
12633             div = doc.documentElement;
12634         } catch (e) {
12635             // old IE... - nasty -- it causes all sorts of issues.. with
12636             // images getting pulled from server..
12637             div = document.createElement('div');
12638             div.innerHTML = this.html;
12639         }
12640         //doc.documentElement.innerHTML = htmlBody
12641          
12642         
12643         
12644         this.tpls = [];
12645         var _t = this;
12646         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12647         
12648         var tpls = this.tpls;
12649         
12650         // create a top level template from the snippet..
12651         
12652         //Roo.log(div.innerHTML);
12653         
12654         var tpl = {
12655             uid : 'master',
12656             id : this.id++,
12657             attr : false,
12658             value : false,
12659             body : div.innerHTML,
12660             
12661             forCall : false,
12662             execCall : false,
12663             dom : div,
12664             isTop : true
12665             
12666         };
12667         tpls.unshift(tpl);
12668         
12669         
12670         // compile them...
12671         this.tpls = [];
12672         Roo.each(tpls, function(tp){
12673             this.compileTpl(tp);
12674             this.tpls[tp.id] = tp;
12675         }, this);
12676         
12677         this.master = tpls[0];
12678         return this;
12679         
12680         
12681     },
12682     
12683     compileNode : function(node, istop) {
12684         // test for
12685         //Roo.log(node);
12686         
12687         
12688         // skip anything not a tag..
12689         if (node.nodeType != 1) {
12690             if (node.nodeType == 3 && !this.inPre) {
12691                 // reduce white space..
12692                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12693                 
12694             }
12695             return;
12696         }
12697         
12698         var tpl = {
12699             uid : false,
12700             id : false,
12701             attr : false,
12702             value : false,
12703             body : '',
12704             
12705             forCall : false,
12706             execCall : false,
12707             dom : false,
12708             isTop : istop
12709             
12710             
12711         };
12712         
12713         
12714         switch(true) {
12715             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12716             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12717             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12718             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12719             // no default..
12720         }
12721         
12722         
12723         if (!tpl.attr) {
12724             // just itterate children..
12725             this.iterChild(node,this.compileNode);
12726             return;
12727         }
12728         tpl.uid = this.id++;
12729         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12730         node.removeAttribute('roo-'+ tpl.attr);
12731         if (tpl.attr != 'name') {
12732             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12733             node.parentNode.replaceChild(placeholder,  node);
12734         } else {
12735             
12736             var placeholder =  document.createElement('span');
12737             placeholder.className = 'roo-tpl-' + tpl.value;
12738             node.parentNode.replaceChild(placeholder,  node);
12739         }
12740         
12741         // parent now sees '{domtplXXXX}
12742         this.iterChild(node,this.compileNode);
12743         
12744         // we should now have node body...
12745         var div = document.createElement('div');
12746         div.appendChild(node);
12747         tpl.dom = node;
12748         // this has the unfortunate side effect of converting tagged attributes
12749         // eg. href="{...}" into %7C...%7D
12750         // this has been fixed by searching for those combo's although it's a bit hacky..
12751         
12752         
12753         tpl.body = div.innerHTML;
12754         
12755         
12756          
12757         tpl.id = tpl.uid;
12758         switch(tpl.attr) {
12759             case 'for' :
12760                 switch (tpl.value) {
12761                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12762                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12763                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12764                 }
12765                 break;
12766             
12767             case 'exec':
12768                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12769                 break;
12770             
12771             case 'if':     
12772                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12773                 break;
12774             
12775             case 'name':
12776                 tpl.id  = tpl.value; // replace non characters???
12777                 break;
12778             
12779         }
12780         
12781         
12782         this.tpls.push(tpl);
12783         
12784         
12785         
12786     },
12787     
12788     
12789     
12790     
12791     /**
12792      * Compile a segment of the template into a 'sub-template'
12793      *
12794      * 
12795      * 
12796      *
12797      */
12798     compileTpl : function(tpl)
12799     {
12800         var fm = Roo.util.Format;
12801         var useF = this.disableFormats !== true;
12802         
12803         var sep = Roo.isGecko ? "+\n" : ",\n";
12804         
12805         var undef = function(str) {
12806             Roo.debug && Roo.log("Property not found :"  + str);
12807             return '';
12808         };
12809           
12810         //Roo.log(tpl.body);
12811         
12812         
12813         
12814         var fn = function(m, lbrace, name, format, args)
12815         {
12816             //Roo.log("ARGS");
12817             //Roo.log(arguments);
12818             args = args ? args.replace(/\\'/g,"'") : args;
12819             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12820             if (typeof(format) == 'undefined') {
12821                 format =  'htmlEncode'; 
12822             }
12823             if (format == 'raw' ) {
12824                 format = false;
12825             }
12826             
12827             if(name.substr(0, 6) == 'domtpl'){
12828                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12829             }
12830             
12831             // build an array of options to determine if value is undefined..
12832             
12833             // basically get 'xxxx.yyyy' then do
12834             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12835             //    (function () { Roo.log("Property not found"); return ''; })() :
12836             //    ......
12837             
12838             var udef_ar = [];
12839             var lookfor = '';
12840             Roo.each(name.split('.'), function(st) {
12841                 lookfor += (lookfor.length ? '.': '') + st;
12842                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12843             });
12844             
12845             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12846             
12847             
12848             if(format && useF){
12849                 
12850                 args = args ? ',' + args : "";
12851                  
12852                 if(format.substr(0, 5) != "this."){
12853                     format = "fm." + format + '(';
12854                 }else{
12855                     format = 'this.call("'+ format.substr(5) + '", ';
12856                     args = ", values";
12857                 }
12858                 
12859                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12860             }
12861              
12862             if (args && args.length) {
12863                 // called with xxyx.yuu:(test,test)
12864                 // change to ()
12865                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12866             }
12867             // raw.. - :raw modifier..
12868             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12869             
12870         };
12871         var body;
12872         // branched to use + in gecko and [].join() in others
12873         if(Roo.isGecko){
12874             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12875                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12876                     "';};};";
12877         }else{
12878             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12879             body.push(tpl.body.replace(/(\r\n|\n)/g,
12880                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12881             body.push("'].join('');};};");
12882             body = body.join('');
12883         }
12884         
12885         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12886        
12887         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12888         eval(body);
12889         
12890         return this;
12891     },
12892      
12893     /**
12894      * same as applyTemplate, except it's done to one of the subTemplates
12895      * when using named templates, you can do:
12896      *
12897      * var str = pl.applySubTemplate('your-name', values);
12898      *
12899      * 
12900      * @param {Number} id of the template
12901      * @param {Object} values to apply to template
12902      * @param {Object} parent (normaly the instance of this object)
12903      */
12904     applySubTemplate : function(id, values, parent)
12905     {
12906         
12907         
12908         var t = this.tpls[id];
12909         
12910         
12911         try { 
12912             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12913                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12914                 return '';
12915             }
12916         } catch(e) {
12917             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12918             Roo.log(values);
12919           
12920             return '';
12921         }
12922         try { 
12923             
12924             if(t.execCall && t.execCall.call(this, values, parent)){
12925                 return '';
12926             }
12927         } catch(e) {
12928             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12929             Roo.log(values);
12930             return '';
12931         }
12932         
12933         try {
12934             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12935             parent = t.target ? values : parent;
12936             if(t.forCall && vs instanceof Array){
12937                 var buf = [];
12938                 for(var i = 0, len = vs.length; i < len; i++){
12939                     try {
12940                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12941                     } catch (e) {
12942                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12943                         Roo.log(e.body);
12944                         //Roo.log(t.compiled);
12945                         Roo.log(vs[i]);
12946                     }   
12947                 }
12948                 return buf.join('');
12949             }
12950         } catch (e) {
12951             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12952             Roo.log(values);
12953             return '';
12954         }
12955         try {
12956             return t.compiled.call(this, vs, parent);
12957         } catch (e) {
12958             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12959             Roo.log(e.body);
12960             //Roo.log(t.compiled);
12961             Roo.log(values);
12962             return '';
12963         }
12964     },
12965
12966    
12967
12968     applyTemplate : function(values){
12969         return this.master.compiled.call(this, values, {});
12970         //var s = this.subs;
12971     },
12972
12973     apply : function(){
12974         return this.applyTemplate.apply(this, arguments);
12975     }
12976
12977  });
12978
12979 Roo.DomTemplate.from = function(el){
12980     el = Roo.getDom(el);
12981     return new Roo.Domtemplate(el.value || el.innerHTML);
12982 };/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993 /**
12994  * @class Roo.util.DelayedTask
12995  * Provides a convenient method of performing setTimeout where a new
12996  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12997  * You can use this class to buffer
12998  * the keypress events for a certain number of milliseconds, and perform only if they stop
12999  * for that amount of time.
13000  * @constructor The parameters to this constructor serve as defaults and are not required.
13001  * @param {Function} fn (optional) The default function to timeout
13002  * @param {Object} scope (optional) The default scope of that timeout
13003  * @param {Array} args (optional) The default Array of arguments
13004  */
13005 Roo.util.DelayedTask = function(fn, scope, args){
13006     var id = null, d, t;
13007
13008     var call = function(){
13009         var now = new Date().getTime();
13010         if(now - t >= d){
13011             clearInterval(id);
13012             id = null;
13013             fn.apply(scope, args || []);
13014         }
13015     };
13016     /**
13017      * Cancels any pending timeout and queues a new one
13018      * @param {Number} delay The milliseconds to delay
13019      * @param {Function} newFn (optional) Overrides function passed to constructor
13020      * @param {Object} newScope (optional) Overrides scope passed to constructor
13021      * @param {Array} newArgs (optional) Overrides args passed to constructor
13022      */
13023     this.delay = function(delay, newFn, newScope, newArgs){
13024         if(id && delay != d){
13025             this.cancel();
13026         }
13027         d = delay;
13028         t = new Date().getTime();
13029         fn = newFn || fn;
13030         scope = newScope || scope;
13031         args = newArgs || args;
13032         if(!id){
13033             id = setInterval(call, d);
13034         }
13035     };
13036
13037     /**
13038      * Cancel the last queued timeout
13039      */
13040     this.cancel = function(){
13041         if(id){
13042             clearInterval(id);
13043             id = null;
13044         }
13045     };
13046 };/*
13047  * Based on:
13048  * Ext JS Library 1.1.1
13049  * Copyright(c) 2006-2007, Ext JS, LLC.
13050  *
13051  * Originally Released Under LGPL - original licence link has changed is not relivant.
13052  *
13053  * Fork - LGPL
13054  * <script type="text/javascript">
13055  */
13056  
13057  
13058 Roo.util.TaskRunner = function(interval){
13059     interval = interval || 10;
13060     var tasks = [], removeQueue = [];
13061     var id = 0;
13062     var running = false;
13063
13064     var stopThread = function(){
13065         running = false;
13066         clearInterval(id);
13067         id = 0;
13068     };
13069
13070     var startThread = function(){
13071         if(!running){
13072             running = true;
13073             id = setInterval(runTasks, interval);
13074         }
13075     };
13076
13077     var removeTask = function(task){
13078         removeQueue.push(task);
13079         if(task.onStop){
13080             task.onStop();
13081         }
13082     };
13083
13084     var runTasks = function(){
13085         if(removeQueue.length > 0){
13086             for(var i = 0, len = removeQueue.length; i < len; i++){
13087                 tasks.remove(removeQueue[i]);
13088             }
13089             removeQueue = [];
13090             if(tasks.length < 1){
13091                 stopThread();
13092                 return;
13093             }
13094         }
13095         var now = new Date().getTime();
13096         for(var i = 0, len = tasks.length; i < len; ++i){
13097             var t = tasks[i];
13098             var itime = now - t.taskRunTime;
13099             if(t.interval <= itime){
13100                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13101                 t.taskRunTime = now;
13102                 if(rt === false || t.taskRunCount === t.repeat){
13103                     removeTask(t);
13104                     return;
13105                 }
13106             }
13107             if(t.duration && t.duration <= (now - t.taskStartTime)){
13108                 removeTask(t);
13109             }
13110         }
13111     };
13112
13113     /**
13114      * Queues a new task.
13115      * @param {Object} task
13116      */
13117     this.start = function(task){
13118         tasks.push(task);
13119         task.taskStartTime = new Date().getTime();
13120         task.taskRunTime = 0;
13121         task.taskRunCount = 0;
13122         startThread();
13123         return task;
13124     };
13125
13126     this.stop = function(task){
13127         removeTask(task);
13128         return task;
13129     };
13130
13131     this.stopAll = function(){
13132         stopThread();
13133         for(var i = 0, len = tasks.length; i < len; i++){
13134             if(tasks[i].onStop){
13135                 tasks[i].onStop();
13136             }
13137         }
13138         tasks = [];
13139         removeQueue = [];
13140     };
13141 };
13142
13143 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13144  * Based on:
13145  * Ext JS Library 1.1.1
13146  * Copyright(c) 2006-2007, Ext JS, LLC.
13147  *
13148  * Originally Released Under LGPL - original licence link has changed is not relivant.
13149  *
13150  * Fork - LGPL
13151  * <script type="text/javascript">
13152  */
13153
13154  
13155 /**
13156  * @class Roo.util.MixedCollection
13157  * @extends Roo.util.Observable
13158  * A Collection class that maintains both numeric indexes and keys and exposes events.
13159  * @constructor
13160  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13161  * collection (defaults to false)
13162  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13163  * and return the key value for that item.  This is used when available to look up the key on items that
13164  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13165  * equivalent to providing an implementation for the {@link #getKey} method.
13166  */
13167 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13168     this.items = [];
13169     this.map = {};
13170     this.keys = [];
13171     this.length = 0;
13172     this.addEvents({
13173         /**
13174          * @event clear
13175          * Fires when the collection is cleared.
13176          */
13177         "clear" : true,
13178         /**
13179          * @event add
13180          * Fires when an item is added to the collection.
13181          * @param {Number} index The index at which the item was added.
13182          * @param {Object} o The item added.
13183          * @param {String} key The key associated with the added item.
13184          */
13185         "add" : true,
13186         /**
13187          * @event replace
13188          * Fires when an item is replaced in the collection.
13189          * @param {String} key he key associated with the new added.
13190          * @param {Object} old The item being replaced.
13191          * @param {Object} new The new item.
13192          */
13193         "replace" : true,
13194         /**
13195          * @event remove
13196          * Fires when an item is removed from the collection.
13197          * @param {Object} o The item being removed.
13198          * @param {String} key (optional) The key associated with the removed item.
13199          */
13200         "remove" : true,
13201         "sort" : true
13202     });
13203     this.allowFunctions = allowFunctions === true;
13204     if(keyFn){
13205         this.getKey = keyFn;
13206     }
13207     Roo.util.MixedCollection.superclass.constructor.call(this);
13208 };
13209
13210 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13211     allowFunctions : false,
13212     
13213 /**
13214  * Adds an item to the collection.
13215  * @param {String} key The key to associate with the item
13216  * @param {Object} o The item to add.
13217  * @return {Object} The item added.
13218  */
13219     add : function(key, o){
13220         if(arguments.length == 1){
13221             o = arguments[0];
13222             key = this.getKey(o);
13223         }
13224         if(typeof key == "undefined" || key === null){
13225             this.length++;
13226             this.items.push(o);
13227             this.keys.push(null);
13228         }else{
13229             var old = this.map[key];
13230             if(old){
13231                 return this.replace(key, o);
13232             }
13233             this.length++;
13234             this.items.push(o);
13235             this.map[key] = o;
13236             this.keys.push(key);
13237         }
13238         this.fireEvent("add", this.length-1, o, key);
13239         return o;
13240     },
13241        
13242 /**
13243   * MixedCollection has a generic way to fetch keys if you implement getKey.
13244 <pre><code>
13245 // normal way
13246 var mc = new Roo.util.MixedCollection();
13247 mc.add(someEl.dom.id, someEl);
13248 mc.add(otherEl.dom.id, otherEl);
13249 //and so on
13250
13251 // using getKey
13252 var mc = new Roo.util.MixedCollection();
13253 mc.getKey = function(el){
13254    return el.dom.id;
13255 };
13256 mc.add(someEl);
13257 mc.add(otherEl);
13258
13259 // or via the constructor
13260 var mc = new Roo.util.MixedCollection(false, function(el){
13261    return el.dom.id;
13262 });
13263 mc.add(someEl);
13264 mc.add(otherEl);
13265 </code></pre>
13266  * @param o {Object} The item for which to find the key.
13267  * @return {Object} The key for the passed item.
13268  */
13269     getKey : function(o){
13270          return o.id; 
13271     },
13272    
13273 /**
13274  * Replaces an item in the collection.
13275  * @param {String} key The key associated with the item to replace, or the item to replace.
13276  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13277  * @return {Object}  The new item.
13278  */
13279     replace : function(key, o){
13280         if(arguments.length == 1){
13281             o = arguments[0];
13282             key = this.getKey(o);
13283         }
13284         var old = this.item(key);
13285         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13286              return this.add(key, o);
13287         }
13288         var index = this.indexOfKey(key);
13289         this.items[index] = o;
13290         this.map[key] = o;
13291         this.fireEvent("replace", key, old, o);
13292         return o;
13293     },
13294    
13295 /**
13296  * Adds all elements of an Array or an Object to the collection.
13297  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13298  * an Array of values, each of which are added to the collection.
13299  */
13300     addAll : function(objs){
13301         if(arguments.length > 1 || objs instanceof Array){
13302             var args = arguments.length > 1 ? arguments : objs;
13303             for(var i = 0, len = args.length; i < len; i++){
13304                 this.add(args[i]);
13305             }
13306         }else{
13307             for(var key in objs){
13308                 if(this.allowFunctions || typeof objs[key] != "function"){
13309                     this.add(key, objs[key]);
13310                 }
13311             }
13312         }
13313     },
13314    
13315 /**
13316  * Executes the specified function once for every item in the collection, passing each
13317  * item as the first and only parameter. returning false from the function will stop the iteration.
13318  * @param {Function} fn The function to execute for each item.
13319  * @param {Object} scope (optional) The scope in which to execute the function.
13320  */
13321     each : function(fn, scope){
13322         var items = [].concat(this.items); // each safe for removal
13323         for(var i = 0, len = items.length; i < len; i++){
13324             if(fn.call(scope || items[i], items[i], i, len) === false){
13325                 break;
13326             }
13327         }
13328     },
13329    
13330 /**
13331  * Executes the specified function once for every key in the collection, passing each
13332  * key, and its associated item as the first two parameters.
13333  * @param {Function} fn The function to execute for each item.
13334  * @param {Object} scope (optional) The scope in which to execute the function.
13335  */
13336     eachKey : function(fn, scope){
13337         for(var i = 0, len = this.keys.length; i < len; i++){
13338             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13339         }
13340     },
13341    
13342 /**
13343  * Returns the first item in the collection which elicits a true return value from the
13344  * passed selection function.
13345  * @param {Function} fn The selection function to execute for each item.
13346  * @param {Object} scope (optional) The scope in which to execute the function.
13347  * @return {Object} The first item in the collection which returned true from the selection function.
13348  */
13349     find : function(fn, scope){
13350         for(var i = 0, len = this.items.length; i < len; i++){
13351             if(fn.call(scope || window, this.items[i], this.keys[i])){
13352                 return this.items[i];
13353             }
13354         }
13355         return null;
13356     },
13357    
13358 /**
13359  * Inserts an item at the specified index in the collection.
13360  * @param {Number} index The index to insert the item at.
13361  * @param {String} key The key to associate with the new item, or the item itself.
13362  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13363  * @return {Object} The item inserted.
13364  */
13365     insert : function(index, key, o){
13366         if(arguments.length == 2){
13367             o = arguments[1];
13368             key = this.getKey(o);
13369         }
13370         if(index >= this.length){
13371             return this.add(key, o);
13372         }
13373         this.length++;
13374         this.items.splice(index, 0, o);
13375         if(typeof key != "undefined" && key != null){
13376             this.map[key] = o;
13377         }
13378         this.keys.splice(index, 0, key);
13379         this.fireEvent("add", index, o, key);
13380         return o;
13381     },
13382    
13383 /**
13384  * Removed an item from the collection.
13385  * @param {Object} o The item to remove.
13386  * @return {Object} The item removed.
13387  */
13388     remove : function(o){
13389         return this.removeAt(this.indexOf(o));
13390     },
13391    
13392 /**
13393  * Remove an item from a specified index in the collection.
13394  * @param {Number} index The index within the collection of the item to remove.
13395  */
13396     removeAt : function(index){
13397         if(index < this.length && index >= 0){
13398             this.length--;
13399             var o = this.items[index];
13400             this.items.splice(index, 1);
13401             var key = this.keys[index];
13402             if(typeof key != "undefined"){
13403                 delete this.map[key];
13404             }
13405             this.keys.splice(index, 1);
13406             this.fireEvent("remove", o, key);
13407         }
13408     },
13409    
13410 /**
13411  * Removed an item associated with the passed key fom the collection.
13412  * @param {String} key The key of the item to remove.
13413  */
13414     removeKey : function(key){
13415         return this.removeAt(this.indexOfKey(key));
13416     },
13417    
13418 /**
13419  * Returns the number of items in the collection.
13420  * @return {Number} the number of items in the collection.
13421  */
13422     getCount : function(){
13423         return this.length; 
13424     },
13425    
13426 /**
13427  * Returns index within the collection of the passed Object.
13428  * @param {Object} o The item to find the index of.
13429  * @return {Number} index of the item.
13430  */
13431     indexOf : function(o){
13432         if(!this.items.indexOf){
13433             for(var i = 0, len = this.items.length; i < len; i++){
13434                 if(this.items[i] == o) {
13435                     return i;
13436                 }
13437             }
13438             return -1;
13439         }else{
13440             return this.items.indexOf(o);
13441         }
13442     },
13443    
13444 /**
13445  * Returns index within the collection of the passed key.
13446  * @param {String} key The key to find the index of.
13447  * @return {Number} index of the key.
13448  */
13449     indexOfKey : function(key){
13450         if(!this.keys.indexOf){
13451             for(var i = 0, len = this.keys.length; i < len; i++){
13452                 if(this.keys[i] == key) {
13453                     return i;
13454                 }
13455             }
13456             return -1;
13457         }else{
13458             return this.keys.indexOf(key);
13459         }
13460     },
13461    
13462 /**
13463  * Returns the item associated with the passed key OR index. Key has priority over index.
13464  * @param {String/Number} key The key or index of the item.
13465  * @return {Object} The item associated with the passed key.
13466  */
13467     item : function(key){
13468         if (key === 'length') {
13469             return null;
13470         }
13471         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13472         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13473     },
13474     
13475 /**
13476  * Returns the item at the specified index.
13477  * @param {Number} index The index of the item.
13478  * @return {Object}
13479  */
13480     itemAt : function(index){
13481         return this.items[index];
13482     },
13483     
13484 /**
13485  * Returns the item associated with the passed key.
13486  * @param {String/Number} key The key of the item.
13487  * @return {Object} The item associated with the passed key.
13488  */
13489     key : function(key){
13490         return this.map[key];
13491     },
13492    
13493 /**
13494  * Returns true if the collection contains the passed Object as an item.
13495  * @param {Object} o  The Object to look for in the collection.
13496  * @return {Boolean} True if the collection contains the Object as an item.
13497  */
13498     contains : function(o){
13499         return this.indexOf(o) != -1;
13500     },
13501    
13502 /**
13503  * Returns true if the collection contains the passed Object as a key.
13504  * @param {String} key The key to look for in the collection.
13505  * @return {Boolean} True if the collection contains the Object as a key.
13506  */
13507     containsKey : function(key){
13508         return typeof this.map[key] != "undefined";
13509     },
13510    
13511 /**
13512  * Removes all items from the collection.
13513  */
13514     clear : function(){
13515         this.length = 0;
13516         this.items = [];
13517         this.keys = [];
13518         this.map = {};
13519         this.fireEvent("clear");
13520     },
13521    
13522 /**
13523  * Returns the first item in the collection.
13524  * @return {Object} the first item in the collection..
13525  */
13526     first : function(){
13527         return this.items[0]; 
13528     },
13529    
13530 /**
13531  * Returns the last item in the collection.
13532  * @return {Object} the last item in the collection..
13533  */
13534     last : function(){
13535         return this.items[this.length-1];   
13536     },
13537     
13538     _sort : function(property, dir, fn){
13539         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13540         fn = fn || function(a, b){
13541             return a-b;
13542         };
13543         var c = [], k = this.keys, items = this.items;
13544         for(var i = 0, len = items.length; i < len; i++){
13545             c[c.length] = {key: k[i], value: items[i], index: i};
13546         }
13547         c.sort(function(a, b){
13548             var v = fn(a[property], b[property]) * dsc;
13549             if(v == 0){
13550                 v = (a.index < b.index ? -1 : 1);
13551             }
13552             return v;
13553         });
13554         for(var i = 0, len = c.length; i < len; i++){
13555             items[i] = c[i].value;
13556             k[i] = c[i].key;
13557         }
13558         this.fireEvent("sort", this);
13559     },
13560     
13561     /**
13562      * Sorts this collection with the passed comparison function
13563      * @param {String} direction (optional) "ASC" or "DESC"
13564      * @param {Function} fn (optional) comparison function
13565      */
13566     sort : function(dir, fn){
13567         this._sort("value", dir, fn);
13568     },
13569     
13570     /**
13571      * Sorts this collection by keys
13572      * @param {String} direction (optional) "ASC" or "DESC"
13573      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13574      */
13575     keySort : function(dir, fn){
13576         this._sort("key", dir, fn || function(a, b){
13577             return String(a).toUpperCase()-String(b).toUpperCase();
13578         });
13579     },
13580     
13581     /**
13582      * Returns a range of items in this collection
13583      * @param {Number} startIndex (optional) defaults to 0
13584      * @param {Number} endIndex (optional) default to the last item
13585      * @return {Array} An array of items
13586      */
13587     getRange : function(start, end){
13588         var items = this.items;
13589         if(items.length < 1){
13590             return [];
13591         }
13592         start = start || 0;
13593         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13594         var r = [];
13595         if(start <= end){
13596             for(var i = start; i <= end; i++) {
13597                     r[r.length] = items[i];
13598             }
13599         }else{
13600             for(var i = start; i >= end; i--) {
13601                     r[r.length] = items[i];
13602             }
13603         }
13604         return r;
13605     },
13606         
13607     /**
13608      * Filter the <i>objects</i> in this collection by a specific property. 
13609      * Returns a new collection that has been filtered.
13610      * @param {String} property A property on your objects
13611      * @param {String/RegExp} value Either string that the property values 
13612      * should start with or a RegExp to test against the property
13613      * @return {MixedCollection} The new filtered collection
13614      */
13615     filter : function(property, value){
13616         if(!value.exec){ // not a regex
13617             value = String(value);
13618             if(value.length == 0){
13619                 return this.clone();
13620             }
13621             value = new RegExp("^" + Roo.escapeRe(value), "i");
13622         }
13623         return this.filterBy(function(o){
13624             return o && value.test(o[property]);
13625         });
13626         },
13627     
13628     /**
13629      * Filter by a function. * Returns a new collection that has been filtered.
13630      * The passed function will be called with each 
13631      * object in the collection. If the function returns true, the value is included 
13632      * otherwise it is filtered.
13633      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13634      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13635      * @return {MixedCollection} The new filtered collection
13636      */
13637     filterBy : function(fn, scope){
13638         var r = new Roo.util.MixedCollection();
13639         r.getKey = this.getKey;
13640         var k = this.keys, it = this.items;
13641         for(var i = 0, len = it.length; i < len; i++){
13642             if(fn.call(scope||this, it[i], k[i])){
13643                                 r.add(k[i], it[i]);
13644                         }
13645         }
13646         return r;
13647     },
13648     
13649     /**
13650      * Creates a duplicate of this collection
13651      * @return {MixedCollection}
13652      */
13653     clone : function(){
13654         var r = new Roo.util.MixedCollection();
13655         var k = this.keys, it = this.items;
13656         for(var i = 0, len = it.length; i < len; i++){
13657             r.add(k[i], it[i]);
13658         }
13659         r.getKey = this.getKey;
13660         return r;
13661     }
13662 });
13663 /**
13664  * Returns the item associated with the passed key or index.
13665  * @method
13666  * @param {String/Number} key The key or index of the item.
13667  * @return {Object} The item associated with the passed key.
13668  */
13669 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13670  * Based on:
13671  * Ext JS Library 1.1.1
13672  * Copyright(c) 2006-2007, Ext JS, LLC.
13673  *
13674  * Originally Released Under LGPL - original licence link has changed is not relivant.
13675  *
13676  * Fork - LGPL
13677  * <script type="text/javascript">
13678  */
13679 /**
13680  * @class Roo.util.JSON
13681  * Modified version of Douglas Crockford"s json.js that doesn"t
13682  * mess with the Object prototype 
13683  * http://www.json.org/js.html
13684  * @singleton
13685  */
13686 Roo.util.JSON = new (function(){
13687     var useHasOwn = {}.hasOwnProperty ? true : false;
13688     
13689     // crashes Safari in some instances
13690     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13691     
13692     var pad = function(n) {
13693         return n < 10 ? "0" + n : n;
13694     };
13695     
13696     var m = {
13697         "\b": '\\b',
13698         "\t": '\\t',
13699         "\n": '\\n',
13700         "\f": '\\f',
13701         "\r": '\\r',
13702         '"' : '\\"',
13703         "\\": '\\\\'
13704     };
13705
13706     var encodeString = function(s){
13707         if (/["\\\x00-\x1f]/.test(s)) {
13708             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13709                 var c = m[b];
13710                 if(c){
13711                     return c;
13712                 }
13713                 c = b.charCodeAt();
13714                 return "\\u00" +
13715                     Math.floor(c / 16).toString(16) +
13716                     (c % 16).toString(16);
13717             }) + '"';
13718         }
13719         return '"' + s + '"';
13720     };
13721     
13722     var encodeArray = function(o){
13723         var a = ["["], b, i, l = o.length, v;
13724             for (i = 0; i < l; i += 1) {
13725                 v = o[i];
13726                 switch (typeof v) {
13727                     case "undefined":
13728                     case "function":
13729                     case "unknown":
13730                         break;
13731                     default:
13732                         if (b) {
13733                             a.push(',');
13734                         }
13735                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13736                         b = true;
13737                 }
13738             }
13739             a.push("]");
13740             return a.join("");
13741     };
13742     
13743     var encodeDate = function(o){
13744         return '"' + o.getFullYear() + "-" +
13745                 pad(o.getMonth() + 1) + "-" +
13746                 pad(o.getDate()) + "T" +
13747                 pad(o.getHours()) + ":" +
13748                 pad(o.getMinutes()) + ":" +
13749                 pad(o.getSeconds()) + '"';
13750     };
13751     
13752     /**
13753      * Encodes an Object, Array or other value
13754      * @param {Mixed} o The variable to encode
13755      * @return {String} The JSON string
13756      */
13757     this.encode = function(o)
13758     {
13759         // should this be extended to fully wrap stringify..
13760         
13761         if(typeof o == "undefined" || o === null){
13762             return "null";
13763         }else if(o instanceof Array){
13764             return encodeArray(o);
13765         }else if(o instanceof Date){
13766             return encodeDate(o);
13767         }else if(typeof o == "string"){
13768             return encodeString(o);
13769         }else if(typeof o == "number"){
13770             return isFinite(o) ? String(o) : "null";
13771         }else if(typeof o == "boolean"){
13772             return String(o);
13773         }else {
13774             var a = ["{"], b, i, v;
13775             for (i in o) {
13776                 if(!useHasOwn || o.hasOwnProperty(i)) {
13777                     v = o[i];
13778                     switch (typeof v) {
13779                     case "undefined":
13780                     case "function":
13781                     case "unknown":
13782                         break;
13783                     default:
13784                         if(b){
13785                             a.push(',');
13786                         }
13787                         a.push(this.encode(i), ":",
13788                                 v === null ? "null" : this.encode(v));
13789                         b = true;
13790                     }
13791                 }
13792             }
13793             a.push("}");
13794             return a.join("");
13795         }
13796     };
13797     
13798     /**
13799      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13800      * @param {String} json The JSON string
13801      * @return {Object} The resulting object
13802      */
13803     this.decode = function(json){
13804         
13805         return  /** eval:var:json */ eval("(" + json + ')');
13806     };
13807 })();
13808 /** 
13809  * Shorthand for {@link Roo.util.JSON#encode}
13810  * @member Roo encode 
13811  * @method */
13812 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13813 /** 
13814  * Shorthand for {@link Roo.util.JSON#decode}
13815  * @member Roo decode 
13816  * @method */
13817 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13818 /*
13819  * Based on:
13820  * Ext JS Library 1.1.1
13821  * Copyright(c) 2006-2007, Ext JS, LLC.
13822  *
13823  * Originally Released Under LGPL - original licence link has changed is not relivant.
13824  *
13825  * Fork - LGPL
13826  * <script type="text/javascript">
13827  */
13828  
13829 /**
13830  * @class Roo.util.Format
13831  * Reusable data formatting functions
13832  * @singleton
13833  */
13834 Roo.util.Format = function(){
13835     var trimRe = /^\s+|\s+$/g;
13836     return {
13837         /**
13838          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13839          * @param {String} value The string to truncate
13840          * @param {Number} length The maximum length to allow before truncating
13841          * @return {String} The converted text
13842          */
13843         ellipsis : function(value, len){
13844             if(value && value.length > len){
13845                 return value.substr(0, len-3)+"...";
13846             }
13847             return value;
13848         },
13849
13850         /**
13851          * Checks a reference and converts it to empty string if it is undefined
13852          * @param {Mixed} value Reference to check
13853          * @return {Mixed} Empty string if converted, otherwise the original value
13854          */
13855         undef : function(value){
13856             return typeof value != "undefined" ? value : "";
13857         },
13858
13859         /**
13860          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13861          * @param {String} value The string to encode
13862          * @return {String} The encoded text
13863          */
13864         htmlEncode : function(value){
13865             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13866         },
13867
13868         /**
13869          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13870          * @param {String} value The string to decode
13871          * @return {String} The decoded text
13872          */
13873         htmlDecode : function(value){
13874             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13875         },
13876
13877         /**
13878          * Trims any whitespace from either side of a string
13879          * @param {String} value The text to trim
13880          * @return {String} The trimmed text
13881          */
13882         trim : function(value){
13883             return String(value).replace(trimRe, "");
13884         },
13885
13886         /**
13887          * Returns a substring from within an original string
13888          * @param {String} value The original text
13889          * @param {Number} start The start index of the substring
13890          * @param {Number} length The length of the substring
13891          * @return {String} The substring
13892          */
13893         substr : function(value, start, length){
13894             return String(value).substr(start, length);
13895         },
13896
13897         /**
13898          * Converts a string to all lower case letters
13899          * @param {String} value The text to convert
13900          * @return {String} The converted text
13901          */
13902         lowercase : function(value){
13903             return String(value).toLowerCase();
13904         },
13905
13906         /**
13907          * Converts a string to all upper case letters
13908          * @param {String} value The text to convert
13909          * @return {String} The converted text
13910          */
13911         uppercase : function(value){
13912             return String(value).toUpperCase();
13913         },
13914
13915         /**
13916          * Converts the first character only of a string to upper case
13917          * @param {String} value The text to convert
13918          * @return {String} The converted text
13919          */
13920         capitalize : function(value){
13921             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13922         },
13923
13924         // private
13925         call : function(value, fn){
13926             if(arguments.length > 2){
13927                 var args = Array.prototype.slice.call(arguments, 2);
13928                 args.unshift(value);
13929                  
13930                 return /** eval:var:value */  eval(fn).apply(window, args);
13931             }else{
13932                 /** eval:var:value */
13933                 return /** eval:var:value */ eval(fn).call(window, value);
13934             }
13935         },
13936
13937        
13938         /**
13939          * safer version of Math.toFixed..??/
13940          * @param {Number/String} value The numeric value to format
13941          * @param {Number/String} value Decimal places 
13942          * @return {String} The formatted currency string
13943          */
13944         toFixed : function(v, n)
13945         {
13946             // why not use to fixed - precision is buggered???
13947             if (!n) {
13948                 return Math.round(v-0);
13949             }
13950             var fact = Math.pow(10,n+1);
13951             v = (Math.round((v-0)*fact))/fact;
13952             var z = (''+fact).substring(2);
13953             if (v == Math.floor(v)) {
13954                 return Math.floor(v) + '.' + z;
13955             }
13956             
13957             // now just padd decimals..
13958             var ps = String(v).split('.');
13959             var fd = (ps[1] + z);
13960             var r = fd.substring(0,n); 
13961             var rm = fd.substring(n); 
13962             if (rm < 5) {
13963                 return ps[0] + '.' + r;
13964             }
13965             r*=1; // turn it into a number;
13966             r++;
13967             if (String(r).length != n) {
13968                 ps[0]*=1;
13969                 ps[0]++;
13970                 r = String(r).substring(1); // chop the end off.
13971             }
13972             
13973             return ps[0] + '.' + r;
13974              
13975         },
13976         
13977         /**
13978          * Format a number as US currency
13979          * @param {Number/String} value The numeric value to format
13980          * @return {String} The formatted currency string
13981          */
13982         usMoney : function(v){
13983             return '$' + Roo.util.Format.number(v);
13984         },
13985         
13986         /**
13987          * Format a number
13988          * eventually this should probably emulate php's number_format
13989          * @param {Number/String} value The numeric value to format
13990          * @param {Number} decimals number of decimal places
13991          * @param {String} delimiter for thousands (default comma)
13992          * @return {String} The formatted currency string
13993          */
13994         number : function(v, decimals, thousandsDelimiter)
13995         {
13996             // multiply and round.
13997             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13998             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13999             
14000             var mul = Math.pow(10, decimals);
14001             var zero = String(mul).substring(1);
14002             v = (Math.round((v-0)*mul))/mul;
14003             
14004             // if it's '0' number.. then
14005             
14006             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14007             v = String(v);
14008             var ps = v.split('.');
14009             var whole = ps[0];
14010             
14011             var r = /(\d+)(\d{3})/;
14012             // add comma's
14013             
14014             if(thousandsDelimiter.length != 0) {
14015                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14016             } 
14017             
14018             var sub = ps[1] ?
14019                     // has decimals..
14020                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14021                     // does not have decimals
14022                     (decimals ? ('.' + zero) : '');
14023             
14024             
14025             return whole + sub ;
14026         },
14027         
14028         /**
14029          * Parse a value into a formatted date using the specified format pattern.
14030          * @param {Mixed} value The value to format
14031          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14032          * @return {String} The formatted date string
14033          */
14034         date : function(v, format){
14035             if(!v){
14036                 return "";
14037             }
14038             if(!(v instanceof Date)){
14039                 v = new Date(Date.parse(v));
14040             }
14041             return v.dateFormat(format || Roo.util.Format.defaults.date);
14042         },
14043
14044         /**
14045          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14046          * @param {String} format Any valid date format string
14047          * @return {Function} The date formatting function
14048          */
14049         dateRenderer : function(format){
14050             return function(v){
14051                 return Roo.util.Format.date(v, format);  
14052             };
14053         },
14054
14055         // private
14056         stripTagsRE : /<\/?[^>]+>/gi,
14057         
14058         /**
14059          * Strips all HTML tags
14060          * @param {Mixed} value The text from which to strip tags
14061          * @return {String} The stripped text
14062          */
14063         stripTags : function(v){
14064             return !v ? v : String(v).replace(this.stripTagsRE, "");
14065         },
14066         
14067         /**
14068          * Size in Mb,Gb etc.
14069          * @param {Number} value The number to be formated
14070          * @param {number} decimals how many decimal places
14071          * @return {String} the formated string
14072          */
14073         size : function(value, decimals)
14074         {
14075             var sizes = ['b', 'k', 'M', 'G', 'T'];
14076             if (value == 0) {
14077                 return 0;
14078             }
14079             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14080             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14081         }
14082         
14083         
14084         
14085     };
14086 }();
14087 Roo.util.Format.defaults = {
14088     date : 'd/M/Y'
14089 };/*
14090  * Based on:
14091  * Ext JS Library 1.1.1
14092  * Copyright(c) 2006-2007, Ext JS, LLC.
14093  *
14094  * Originally Released Under LGPL - original licence link has changed is not relivant.
14095  *
14096  * Fork - LGPL
14097  * <script type="text/javascript">
14098  */
14099
14100
14101  
14102
14103 /**
14104  * @class Roo.MasterTemplate
14105  * @extends Roo.Template
14106  * Provides a template that can have child templates. The syntax is:
14107 <pre><code>
14108 var t = new Roo.MasterTemplate(
14109         '&lt;select name="{name}"&gt;',
14110                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14111         '&lt;/select&gt;'
14112 );
14113 t.add('options', {value: 'foo', text: 'bar'});
14114 // or you can add multiple child elements in one shot
14115 t.addAll('options', [
14116     {value: 'foo', text: 'bar'},
14117     {value: 'foo2', text: 'bar2'},
14118     {value: 'foo3', text: 'bar3'}
14119 ]);
14120 // then append, applying the master template values
14121 t.append('my-form', {name: 'my-select'});
14122 </code></pre>
14123 * A name attribute for the child template is not required if you have only one child
14124 * template or you want to refer to them by index.
14125  */
14126 Roo.MasterTemplate = function(){
14127     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14128     this.originalHtml = this.html;
14129     var st = {};
14130     var m, re = this.subTemplateRe;
14131     re.lastIndex = 0;
14132     var subIndex = 0;
14133     while(m = re.exec(this.html)){
14134         var name = m[1], content = m[2];
14135         st[subIndex] = {
14136             name: name,
14137             index: subIndex,
14138             buffer: [],
14139             tpl : new Roo.Template(content)
14140         };
14141         if(name){
14142             st[name] = st[subIndex];
14143         }
14144         st[subIndex].tpl.compile();
14145         st[subIndex].tpl.call = this.call.createDelegate(this);
14146         subIndex++;
14147     }
14148     this.subCount = subIndex;
14149     this.subs = st;
14150 };
14151 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14152     /**
14153     * The regular expression used to match sub templates
14154     * @type RegExp
14155     * @property
14156     */
14157     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14158
14159     /**
14160      * Applies the passed values to a child template.
14161      * @param {String/Number} name (optional) The name or index of the child template
14162      * @param {Array/Object} values The values to be applied to the template
14163      * @return {MasterTemplate} this
14164      */
14165      add : function(name, values){
14166         if(arguments.length == 1){
14167             values = arguments[0];
14168             name = 0;
14169         }
14170         var s = this.subs[name];
14171         s.buffer[s.buffer.length] = s.tpl.apply(values);
14172         return this;
14173     },
14174
14175     /**
14176      * Applies all the passed values to a child template.
14177      * @param {String/Number} name (optional) The name or index of the child template
14178      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14179      * @param {Boolean} reset (optional) True to reset the template first
14180      * @return {MasterTemplate} this
14181      */
14182     fill : function(name, values, reset){
14183         var a = arguments;
14184         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14185             values = a[0];
14186             name = 0;
14187             reset = a[1];
14188         }
14189         if(reset){
14190             this.reset();
14191         }
14192         for(var i = 0, len = values.length; i < len; i++){
14193             this.add(name, values[i]);
14194         }
14195         return this;
14196     },
14197
14198     /**
14199      * Resets the template for reuse
14200      * @return {MasterTemplate} this
14201      */
14202      reset : function(){
14203         var s = this.subs;
14204         for(var i = 0; i < this.subCount; i++){
14205             s[i].buffer = [];
14206         }
14207         return this;
14208     },
14209
14210     applyTemplate : function(values){
14211         var s = this.subs;
14212         var replaceIndex = -1;
14213         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14214             return s[++replaceIndex].buffer.join("");
14215         });
14216         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14217     },
14218
14219     apply : function(){
14220         return this.applyTemplate.apply(this, arguments);
14221     },
14222
14223     compile : function(){return this;}
14224 });
14225
14226 /**
14227  * Alias for fill().
14228  * @method
14229  */
14230 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14231  /**
14232  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14233  * var tpl = Roo.MasterTemplate.from('element-id');
14234  * @param {String/HTMLElement} el
14235  * @param {Object} config
14236  * @static
14237  */
14238 Roo.MasterTemplate.from = function(el, config){
14239     el = Roo.getDom(el);
14240     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14241 };/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252  
14253 /**
14254  * @class Roo.util.CSS
14255  * Utility class for manipulating CSS rules
14256  * @singleton
14257  */
14258 Roo.util.CSS = function(){
14259         var rules = null;
14260         var doc = document;
14261
14262     var camelRe = /(-[a-z])/gi;
14263     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14264
14265    return {
14266    /**
14267     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14268     * tag and appended to the HEAD of the document.
14269     * @param {String|Object} cssText The text containing the css rules
14270     * @param {String} id An id to add to the stylesheet for later removal
14271     * @return {StyleSheet}
14272     */
14273     createStyleSheet : function(cssText, id){
14274         var ss;
14275         var head = doc.getElementsByTagName("head")[0];
14276         var nrules = doc.createElement("style");
14277         nrules.setAttribute("type", "text/css");
14278         if(id){
14279             nrules.setAttribute("id", id);
14280         }
14281         if (typeof(cssText) != 'string') {
14282             // support object maps..
14283             // not sure if this a good idea.. 
14284             // perhaps it should be merged with the general css handling
14285             // and handle js style props.
14286             var cssTextNew = [];
14287             for(var n in cssText) {
14288                 var citems = [];
14289                 for(var k in cssText[n]) {
14290                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14291                 }
14292                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14293                 
14294             }
14295             cssText = cssTextNew.join("\n");
14296             
14297         }
14298        
14299        
14300        if(Roo.isIE){
14301            head.appendChild(nrules);
14302            ss = nrules.styleSheet;
14303            ss.cssText = cssText;
14304        }else{
14305            try{
14306                 nrules.appendChild(doc.createTextNode(cssText));
14307            }catch(e){
14308                nrules.cssText = cssText; 
14309            }
14310            head.appendChild(nrules);
14311            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14312        }
14313        this.cacheStyleSheet(ss);
14314        return ss;
14315    },
14316
14317    /**
14318     * Removes a style or link tag by id
14319     * @param {String} id The id of the tag
14320     */
14321    removeStyleSheet : function(id){
14322        var existing = doc.getElementById(id);
14323        if(existing){
14324            existing.parentNode.removeChild(existing);
14325        }
14326    },
14327
14328    /**
14329     * Dynamically swaps an existing stylesheet reference for a new one
14330     * @param {String} id The id of an existing link tag to remove
14331     * @param {String} url The href of the new stylesheet to include
14332     */
14333    swapStyleSheet : function(id, url){
14334        this.removeStyleSheet(id);
14335        var ss = doc.createElement("link");
14336        ss.setAttribute("rel", "stylesheet");
14337        ss.setAttribute("type", "text/css");
14338        ss.setAttribute("id", id);
14339        ss.setAttribute("href", url);
14340        doc.getElementsByTagName("head")[0].appendChild(ss);
14341    },
14342    
14343    /**
14344     * Refresh the rule cache if you have dynamically added stylesheets
14345     * @return {Object} An object (hash) of rules indexed by selector
14346     */
14347    refreshCache : function(){
14348        return this.getRules(true);
14349    },
14350
14351    // private
14352    cacheStyleSheet : function(stylesheet){
14353        if(!rules){
14354            rules = {};
14355        }
14356        try{// try catch for cross domain access issue
14357            var ssRules = stylesheet.cssRules || stylesheet.rules;
14358            for(var j = ssRules.length-1; j >= 0; --j){
14359                rules[ssRules[j].selectorText] = ssRules[j];
14360            }
14361        }catch(e){}
14362    },
14363    
14364    /**
14365     * Gets all css rules for the document
14366     * @param {Boolean} refreshCache true to refresh the internal cache
14367     * @return {Object} An object (hash) of rules indexed by selector
14368     */
14369    getRules : function(refreshCache){
14370                 if(rules == null || refreshCache){
14371                         rules = {};
14372                         var ds = doc.styleSheets;
14373                         for(var i =0, len = ds.length; i < len; i++){
14374                             try{
14375                         this.cacheStyleSheet(ds[i]);
14376                     }catch(e){} 
14377                 }
14378                 }
14379                 return rules;
14380         },
14381         
14382         /**
14383     * Gets an an individual CSS rule by selector(s)
14384     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14385     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14386     * @return {CSSRule} The CSS rule or null if one is not found
14387     */
14388    getRule : function(selector, refreshCache){
14389                 var rs = this.getRules(refreshCache);
14390                 if(!(selector instanceof Array)){
14391                     return rs[selector];
14392                 }
14393                 for(var i = 0; i < selector.length; i++){
14394                         if(rs[selector[i]]){
14395                                 return rs[selector[i]];
14396                         }
14397                 }
14398                 return null;
14399         },
14400         
14401         
14402         /**
14403     * Updates a rule property
14404     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14405     * @param {String} property The css property
14406     * @param {String} value The new value for the property
14407     * @return {Boolean} true If a rule was found and updated
14408     */
14409    updateRule : function(selector, property, value){
14410                 if(!(selector instanceof Array)){
14411                         var rule = this.getRule(selector);
14412                         if(rule){
14413                                 rule.style[property.replace(camelRe, camelFn)] = value;
14414                                 return true;
14415                         }
14416                 }else{
14417                         for(var i = 0; i < selector.length; i++){
14418                                 if(this.updateRule(selector[i], property, value)){
14419                                         return true;
14420                                 }
14421                         }
14422                 }
14423                 return false;
14424         }
14425    };   
14426 }();/*
14427  * Based on:
14428  * Ext JS Library 1.1.1
14429  * Copyright(c) 2006-2007, Ext JS, LLC.
14430  *
14431  * Originally Released Under LGPL - original licence link has changed is not relivant.
14432  *
14433  * Fork - LGPL
14434  * <script type="text/javascript">
14435  */
14436
14437  
14438
14439 /**
14440  * @class Roo.util.ClickRepeater
14441  * @extends Roo.util.Observable
14442  * 
14443  * A wrapper class which can be applied to any element. Fires a "click" event while the
14444  * mouse is pressed. The interval between firings may be specified in the config but
14445  * defaults to 10 milliseconds.
14446  * 
14447  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14448  * 
14449  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14450  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14451  * Similar to an autorepeat key delay.
14452  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14453  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14454  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14455  *           "interval" and "delay" are ignored. "immediate" is honored.
14456  * @cfg {Boolean} preventDefault True to prevent the default click event
14457  * @cfg {Boolean} stopDefault True to stop the default click event
14458  * 
14459  * @history
14460  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14461  *     2007-02-02 jvs Renamed to ClickRepeater
14462  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14463  *
14464  *  @constructor
14465  * @param {String/HTMLElement/Element} el The element to listen on
14466  * @param {Object} config
14467  **/
14468 Roo.util.ClickRepeater = function(el, config)
14469 {
14470     this.el = Roo.get(el);
14471     this.el.unselectable();
14472
14473     Roo.apply(this, config);
14474
14475     this.addEvents({
14476     /**
14477      * @event mousedown
14478      * Fires when the mouse button is depressed.
14479      * @param {Roo.util.ClickRepeater} this
14480      */
14481         "mousedown" : true,
14482     /**
14483      * @event click
14484      * Fires on a specified interval during the time the element is pressed.
14485      * @param {Roo.util.ClickRepeater} this
14486      */
14487         "click" : true,
14488     /**
14489      * @event mouseup
14490      * Fires when the mouse key is released.
14491      * @param {Roo.util.ClickRepeater} this
14492      */
14493         "mouseup" : true
14494     });
14495
14496     this.el.on("mousedown", this.handleMouseDown, this);
14497     if(this.preventDefault || this.stopDefault){
14498         this.el.on("click", function(e){
14499             if(this.preventDefault){
14500                 e.preventDefault();
14501             }
14502             if(this.stopDefault){
14503                 e.stopEvent();
14504             }
14505         }, this);
14506     }
14507
14508     // allow inline handler
14509     if(this.handler){
14510         this.on("click", this.handler,  this.scope || this);
14511     }
14512
14513     Roo.util.ClickRepeater.superclass.constructor.call(this);
14514 };
14515
14516 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14517     interval : 20,
14518     delay: 250,
14519     preventDefault : true,
14520     stopDefault : false,
14521     timer : 0,
14522
14523     // private
14524     handleMouseDown : function(){
14525         clearTimeout(this.timer);
14526         this.el.blur();
14527         if(this.pressClass){
14528             this.el.addClass(this.pressClass);
14529         }
14530         this.mousedownTime = new Date();
14531
14532         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14533         this.el.on("mouseout", this.handleMouseOut, this);
14534
14535         this.fireEvent("mousedown", this);
14536         this.fireEvent("click", this);
14537         
14538         this.timer = this.click.defer(this.delay || this.interval, this);
14539     },
14540
14541     // private
14542     click : function(){
14543         this.fireEvent("click", this);
14544         this.timer = this.click.defer(this.getInterval(), this);
14545     },
14546
14547     // private
14548     getInterval: function(){
14549         if(!this.accelerate){
14550             return this.interval;
14551         }
14552         var pressTime = this.mousedownTime.getElapsed();
14553         if(pressTime < 500){
14554             return 400;
14555         }else if(pressTime < 1700){
14556             return 320;
14557         }else if(pressTime < 2600){
14558             return 250;
14559         }else if(pressTime < 3500){
14560             return 180;
14561         }else if(pressTime < 4400){
14562             return 140;
14563         }else if(pressTime < 5300){
14564             return 80;
14565         }else if(pressTime < 6200){
14566             return 50;
14567         }else{
14568             return 10;
14569         }
14570     },
14571
14572     // private
14573     handleMouseOut : function(){
14574         clearTimeout(this.timer);
14575         if(this.pressClass){
14576             this.el.removeClass(this.pressClass);
14577         }
14578         this.el.on("mouseover", this.handleMouseReturn, this);
14579     },
14580
14581     // private
14582     handleMouseReturn : function(){
14583         this.el.un("mouseover", this.handleMouseReturn);
14584         if(this.pressClass){
14585             this.el.addClass(this.pressClass);
14586         }
14587         this.click();
14588     },
14589
14590     // private
14591     handleMouseUp : function(){
14592         clearTimeout(this.timer);
14593         this.el.un("mouseover", this.handleMouseReturn);
14594         this.el.un("mouseout", this.handleMouseOut);
14595         Roo.get(document).un("mouseup", this.handleMouseUp);
14596         this.el.removeClass(this.pressClass);
14597         this.fireEvent("mouseup", this);
14598     }
14599 });/**
14600  * @class Roo.util.Clipboard
14601  * @static
14602  * 
14603  * Clipboard UTILS
14604  * 
14605  **/
14606 Roo.util.Clipboard = {
14607     /**
14608      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14609      * @param {String} text to copy to clipboard
14610      */
14611     write : function(text) {
14612         // navigator clipboard api needs a secure context (https)
14613         if (navigator.clipboard && window.isSecureContext) {
14614             // navigator clipboard api method'
14615             navigator.clipboard.writeText(text);
14616             return ;
14617         } 
14618         // text area method
14619         var ta = document.createElement("textarea");
14620         ta.value = text;
14621         // make the textarea out of viewport
14622         ta.style.position = "fixed";
14623         ta.style.left = "-999999px";
14624         ta.style.top = "-999999px";
14625         document.body.appendChild(ta);
14626         ta.focus();
14627         ta.select();
14628         document.execCommand('copy');
14629         (function() {
14630             ta.remove();
14631         }).defer(100);
14632         
14633     }
14634         
14635 }
14636     /*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646
14647  
14648 /**
14649  * @class Roo.KeyNav
14650  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14651  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14652  * way to implement custom navigation schemes for any UI component.</p>
14653  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14654  * pageUp, pageDown, del, home, end.  Usage:</p>
14655  <pre><code>
14656 var nav = new Roo.KeyNav("my-element", {
14657     "left" : function(e){
14658         this.moveLeft(e.ctrlKey);
14659     },
14660     "right" : function(e){
14661         this.moveRight(e.ctrlKey);
14662     },
14663     "enter" : function(e){
14664         this.save();
14665     },
14666     scope : this
14667 });
14668 </code></pre>
14669  * @constructor
14670  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14671  * @param {Object} config The config
14672  */
14673 Roo.KeyNav = function(el, config){
14674     this.el = Roo.get(el);
14675     Roo.apply(this, config);
14676     if(!this.disabled){
14677         this.disabled = true;
14678         this.enable();
14679     }
14680 };
14681
14682 Roo.KeyNav.prototype = {
14683     /**
14684      * @cfg {Boolean} disabled
14685      * True to disable this KeyNav instance (defaults to false)
14686      */
14687     disabled : false,
14688     /**
14689      * @cfg {String} defaultEventAction
14690      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14691      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14692      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14693      */
14694     defaultEventAction: "stopEvent",
14695     /**
14696      * @cfg {Boolean} forceKeyDown
14697      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14698      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14699      * handle keydown instead of keypress.
14700      */
14701     forceKeyDown : false,
14702
14703     // private
14704     prepareEvent : function(e){
14705         var k = e.getKey();
14706         var h = this.keyToHandler[k];
14707         //if(h && this[h]){
14708         //    e.stopPropagation();
14709         //}
14710         if(Roo.isSafari && h && k >= 37 && k <= 40){
14711             e.stopEvent();
14712         }
14713     },
14714
14715     // private
14716     relay : function(e){
14717         var k = e.getKey();
14718         var h = this.keyToHandler[k];
14719         if(h && this[h]){
14720             if(this.doRelay(e, this[h], h) !== true){
14721                 e[this.defaultEventAction]();
14722             }
14723         }
14724     },
14725
14726     // private
14727     doRelay : function(e, h, hname){
14728         return h.call(this.scope || this, e);
14729     },
14730
14731     // possible handlers
14732     enter : false,
14733     left : false,
14734     right : false,
14735     up : false,
14736     down : false,
14737     tab : false,
14738     esc : false,
14739     pageUp : false,
14740     pageDown : false,
14741     del : false,
14742     home : false,
14743     end : false,
14744
14745     // quick lookup hash
14746     keyToHandler : {
14747         37 : "left",
14748         39 : "right",
14749         38 : "up",
14750         40 : "down",
14751         33 : "pageUp",
14752         34 : "pageDown",
14753         46 : "del",
14754         36 : "home",
14755         35 : "end",
14756         13 : "enter",
14757         27 : "esc",
14758         9  : "tab"
14759     },
14760
14761         /**
14762          * Enable this KeyNav
14763          */
14764         enable: function(){
14765                 if(this.disabled){
14766             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14767             // the EventObject will normalize Safari automatically
14768             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14769                 this.el.on("keydown", this.relay,  this);
14770             }else{
14771                 this.el.on("keydown", this.prepareEvent,  this);
14772                 this.el.on("keypress", this.relay,  this);
14773             }
14774                     this.disabled = false;
14775                 }
14776         },
14777
14778         /**
14779          * Disable this KeyNav
14780          */
14781         disable: function(){
14782                 if(!this.disabled){
14783                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14784                 this.el.un("keydown", this.relay);
14785             }else{
14786                 this.el.un("keydown", this.prepareEvent);
14787                 this.el.un("keypress", this.relay);
14788             }
14789                     this.disabled = true;
14790                 }
14791         }
14792 };/*
14793  * Based on:
14794  * Ext JS Library 1.1.1
14795  * Copyright(c) 2006-2007, Ext JS, LLC.
14796  *
14797  * Originally Released Under LGPL - original licence link has changed is not relivant.
14798  *
14799  * Fork - LGPL
14800  * <script type="text/javascript">
14801  */
14802
14803  
14804 /**
14805  * @class Roo.KeyMap
14806  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14807  * The constructor accepts the same config object as defined by {@link #addBinding}.
14808  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14809  * combination it will call the function with this signature (if the match is a multi-key
14810  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14811  * A KeyMap can also handle a string representation of keys.<br />
14812  * Usage:
14813  <pre><code>
14814 // map one key by key code
14815 var map = new Roo.KeyMap("my-element", {
14816     key: 13, // or Roo.EventObject.ENTER
14817     fn: myHandler,
14818     scope: myObject
14819 });
14820
14821 // map multiple keys to one action by string
14822 var map = new Roo.KeyMap("my-element", {
14823     key: "a\r\n\t",
14824     fn: myHandler,
14825     scope: myObject
14826 });
14827
14828 // map multiple keys to multiple actions by strings and array of codes
14829 var map = new Roo.KeyMap("my-element", [
14830     {
14831         key: [10,13],
14832         fn: function(){ alert("Return was pressed"); }
14833     }, {
14834         key: "abc",
14835         fn: function(){ alert('a, b or c was pressed'); }
14836     }, {
14837         key: "\t",
14838         ctrl:true,
14839         shift:true,
14840         fn: function(){ alert('Control + shift + tab was pressed.'); }
14841     }
14842 ]);
14843 </code></pre>
14844  * <b>Note: A KeyMap starts enabled</b>
14845  * @constructor
14846  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14847  * @param {Object} config The config (see {@link #addBinding})
14848  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14849  */
14850 Roo.KeyMap = function(el, config, eventName){
14851     this.el  = Roo.get(el);
14852     this.eventName = eventName || "keydown";
14853     this.bindings = [];
14854     if(config){
14855         this.addBinding(config);
14856     }
14857     this.enable();
14858 };
14859
14860 Roo.KeyMap.prototype = {
14861     /**
14862      * True to stop the event from bubbling and prevent the default browser action if the
14863      * key was handled by the KeyMap (defaults to false)
14864      * @type Boolean
14865      */
14866     stopEvent : false,
14867
14868     /**
14869      * Add a new binding to this KeyMap. The following config object properties are supported:
14870      * <pre>
14871 Property    Type             Description
14872 ----------  ---------------  ----------------------------------------------------------------------
14873 key         String/Array     A single keycode or an array of keycodes to handle
14874 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14875 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14876 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14877 fn          Function         The function to call when KeyMap finds the expected key combination
14878 scope       Object           The scope of the callback function
14879 </pre>
14880      *
14881      * Usage:
14882      * <pre><code>
14883 // Create a KeyMap
14884 var map = new Roo.KeyMap(document, {
14885     key: Roo.EventObject.ENTER,
14886     fn: handleKey,
14887     scope: this
14888 });
14889
14890 //Add a new binding to the existing KeyMap later
14891 map.addBinding({
14892     key: 'abc',
14893     shift: true,
14894     fn: handleKey,
14895     scope: this
14896 });
14897 </code></pre>
14898      * @param {Object/Array} config A single KeyMap config or an array of configs
14899      */
14900         addBinding : function(config){
14901         if(config instanceof Array){
14902             for(var i = 0, len = config.length; i < len; i++){
14903                 this.addBinding(config[i]);
14904             }
14905             return;
14906         }
14907         var keyCode = config.key,
14908             shift = config.shift, 
14909             ctrl = config.ctrl, 
14910             alt = config.alt,
14911             fn = config.fn,
14912             scope = config.scope;
14913         if(typeof keyCode == "string"){
14914             var ks = [];
14915             var keyString = keyCode.toUpperCase();
14916             for(var j = 0, len = keyString.length; j < len; j++){
14917                 ks.push(keyString.charCodeAt(j));
14918             }
14919             keyCode = ks;
14920         }
14921         var keyArray = keyCode instanceof Array;
14922         var handler = function(e){
14923             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14924                 var k = e.getKey();
14925                 if(keyArray){
14926                     for(var i = 0, len = keyCode.length; i < len; i++){
14927                         if(keyCode[i] == k){
14928                           if(this.stopEvent){
14929                               e.stopEvent();
14930                           }
14931                           fn.call(scope || window, k, e);
14932                           return;
14933                         }
14934                     }
14935                 }else{
14936                     if(k == keyCode){
14937                         if(this.stopEvent){
14938                            e.stopEvent();
14939                         }
14940                         fn.call(scope || window, k, e);
14941                     }
14942                 }
14943             }
14944         };
14945         this.bindings.push(handler);  
14946         },
14947
14948     /**
14949      * Shorthand for adding a single key listener
14950      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14951      * following options:
14952      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14953      * @param {Function} fn The function to call
14954      * @param {Object} scope (optional) The scope of the function
14955      */
14956     on : function(key, fn, scope){
14957         var keyCode, shift, ctrl, alt;
14958         if(typeof key == "object" && !(key instanceof Array)){
14959             keyCode = key.key;
14960             shift = key.shift;
14961             ctrl = key.ctrl;
14962             alt = key.alt;
14963         }else{
14964             keyCode = key;
14965         }
14966         this.addBinding({
14967             key: keyCode,
14968             shift: shift,
14969             ctrl: ctrl,
14970             alt: alt,
14971             fn: fn,
14972             scope: scope
14973         })
14974     },
14975
14976     // private
14977     handleKeyDown : function(e){
14978             if(this.enabled){ //just in case
14979             var b = this.bindings;
14980             for(var i = 0, len = b.length; i < len; i++){
14981                 b[i].call(this, e);
14982             }
14983             }
14984         },
14985         
14986         /**
14987          * Returns true if this KeyMap is enabled
14988          * @return {Boolean} 
14989          */
14990         isEnabled : function(){
14991             return this.enabled;  
14992         },
14993         
14994         /**
14995          * Enables this KeyMap
14996          */
14997         enable: function(){
14998                 if(!this.enabled){
14999                     this.el.on(this.eventName, this.handleKeyDown, this);
15000                     this.enabled = true;
15001                 }
15002         },
15003
15004         /**
15005          * Disable this KeyMap
15006          */
15007         disable: function(){
15008                 if(this.enabled){
15009                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15010                     this.enabled = false;
15011                 }
15012         }
15013 };/*
15014  * Based on:
15015  * Ext JS Library 1.1.1
15016  * Copyright(c) 2006-2007, Ext JS, LLC.
15017  *
15018  * Originally Released Under LGPL - original licence link has changed is not relivant.
15019  *
15020  * Fork - LGPL
15021  * <script type="text/javascript">
15022  */
15023
15024  
15025 /**
15026  * @class Roo.util.TextMetrics
15027  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15028  * wide, in pixels, a given block of text will be.
15029  * @singleton
15030  */
15031 Roo.util.TextMetrics = function(){
15032     var shared;
15033     return {
15034         /**
15035          * Measures the size of the specified text
15036          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15037          * that can affect the size of the rendered text
15038          * @param {String} text The text to measure
15039          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15040          * in order to accurately measure the text height
15041          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15042          */
15043         measure : function(el, text, fixedWidth){
15044             if(!shared){
15045                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15046             }
15047             shared.bind(el);
15048             shared.setFixedWidth(fixedWidth || 'auto');
15049             return shared.getSize(text);
15050         },
15051
15052         /**
15053          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15054          * the overhead of multiple calls to initialize the style properties on each measurement.
15055          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15056          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15057          * in order to accurately measure the text height
15058          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15059          */
15060         createInstance : function(el, fixedWidth){
15061             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15062         }
15063     };
15064 }();
15065
15066  
15067
15068 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15069     var ml = new Roo.Element(document.createElement('div'));
15070     document.body.appendChild(ml.dom);
15071     ml.position('absolute');
15072     ml.setLeftTop(-1000, -1000);
15073     ml.hide();
15074
15075     if(fixedWidth){
15076         ml.setWidth(fixedWidth);
15077     }
15078      
15079     var instance = {
15080         /**
15081          * Returns the size of the specified text based on the internal element's style and width properties
15082          * @memberOf Roo.util.TextMetrics.Instance#
15083          * @param {String} text The text to measure
15084          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15085          */
15086         getSize : function(text){
15087             ml.update(text);
15088             var s = ml.getSize();
15089             ml.update('');
15090             return s;
15091         },
15092
15093         /**
15094          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15095          * that can affect the size of the rendered text
15096          * @memberOf Roo.util.TextMetrics.Instance#
15097          * @param {String/HTMLElement} el The element, dom node or id
15098          */
15099         bind : function(el){
15100             ml.setStyle(
15101                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15102             );
15103         },
15104
15105         /**
15106          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15107          * to set a fixed width in order to accurately measure the text height.
15108          * @memberOf Roo.util.TextMetrics.Instance#
15109          * @param {Number} width The width to set on the element
15110          */
15111         setFixedWidth : function(width){
15112             ml.setWidth(width);
15113         },
15114
15115         /**
15116          * Returns the measured width of the specified text
15117          * @memberOf Roo.util.TextMetrics.Instance#
15118          * @param {String} text The text to measure
15119          * @return {Number} width The width in pixels
15120          */
15121         getWidth : function(text){
15122             ml.dom.style.width = 'auto';
15123             return this.getSize(text).width;
15124         },
15125
15126         /**
15127          * Returns the measured height of the specified text.  For multiline text, be sure to call
15128          * {@link #setFixedWidth} if necessary.
15129          * @memberOf Roo.util.TextMetrics.Instance#
15130          * @param {String} text The text to measure
15131          * @return {Number} height The height in pixels
15132          */
15133         getHeight : function(text){
15134             return this.getSize(text).height;
15135         }
15136     };
15137
15138     instance.bind(bindTo);
15139
15140     return instance;
15141 };
15142
15143 // backwards compat
15144 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15145  * Based on:
15146  * Ext JS Library 1.1.1
15147  * Copyright(c) 2006-2007, Ext JS, LLC.
15148  *
15149  * Originally Released Under LGPL - original licence link has changed is not relivant.
15150  *
15151  * Fork - LGPL
15152  * <script type="text/javascript">
15153  */
15154
15155 /**
15156  * @class Roo.state.Provider
15157  * Abstract base class for state provider implementations. This class provides methods
15158  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15159  * Provider interface.
15160  */
15161 Roo.state.Provider = function(){
15162     /**
15163      * @event statechange
15164      * Fires when a state change occurs.
15165      * @param {Provider} this This state provider
15166      * @param {String} key The state key which was changed
15167      * @param {String} value The encoded value for the state
15168      */
15169     this.addEvents({
15170         "statechange": true
15171     });
15172     this.state = {};
15173     Roo.state.Provider.superclass.constructor.call(this);
15174 };
15175 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15176     /**
15177      * Returns the current value for a key
15178      * @param {String} name The key name
15179      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15180      * @return {Mixed} The state data
15181      */
15182     get : function(name, defaultValue){
15183         return typeof this.state[name] == "undefined" ?
15184             defaultValue : this.state[name];
15185     },
15186     
15187     /**
15188      * Clears a value from the state
15189      * @param {String} name The key name
15190      */
15191     clear : function(name){
15192         delete this.state[name];
15193         this.fireEvent("statechange", this, name, null);
15194     },
15195     
15196     /**
15197      * Sets the value for a key
15198      * @param {String} name The key name
15199      * @param {Mixed} value The value to set
15200      */
15201     set : function(name, value){
15202         this.state[name] = value;
15203         this.fireEvent("statechange", this, name, value);
15204     },
15205     
15206     /**
15207      * Decodes a string previously encoded with {@link #encodeValue}.
15208      * @param {String} value The value to decode
15209      * @return {Mixed} The decoded value
15210      */
15211     decodeValue : function(cookie){
15212         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15213         var matches = re.exec(unescape(cookie));
15214         if(!matches || !matches[1]) {
15215             return; // non state cookie
15216         }
15217         var type = matches[1];
15218         var v = matches[2];
15219         switch(type){
15220             case "n":
15221                 return parseFloat(v);
15222             case "d":
15223                 return new Date(Date.parse(v));
15224             case "b":
15225                 return (v == "1");
15226             case "a":
15227                 var all = [];
15228                 var values = v.split("^");
15229                 for(var i = 0, len = values.length; i < len; i++){
15230                     all.push(this.decodeValue(values[i]));
15231                 }
15232                 return all;
15233            case "o":
15234                 var all = {};
15235                 var values = v.split("^");
15236                 for(var i = 0, len = values.length; i < len; i++){
15237                     var kv = values[i].split("=");
15238                     all[kv[0]] = this.decodeValue(kv[1]);
15239                 }
15240                 return all;
15241            default:
15242                 return v;
15243         }
15244     },
15245     
15246     /**
15247      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15248      * @param {Mixed} value The value to encode
15249      * @return {String} The encoded value
15250      */
15251     encodeValue : function(v){
15252         var enc;
15253         if(typeof v == "number"){
15254             enc = "n:" + v;
15255         }else if(typeof v == "boolean"){
15256             enc = "b:" + (v ? "1" : "0");
15257         }else if(v instanceof Date){
15258             enc = "d:" + v.toGMTString();
15259         }else if(v instanceof Array){
15260             var flat = "";
15261             for(var i = 0, len = v.length; i < len; i++){
15262                 flat += this.encodeValue(v[i]);
15263                 if(i != len-1) {
15264                     flat += "^";
15265                 }
15266             }
15267             enc = "a:" + flat;
15268         }else if(typeof v == "object"){
15269             var flat = "";
15270             for(var key in v){
15271                 if(typeof v[key] != "function"){
15272                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15273                 }
15274             }
15275             enc = "o:" + flat.substring(0, flat.length-1);
15276         }else{
15277             enc = "s:" + v;
15278         }
15279         return escape(enc);        
15280     }
15281 });
15282
15283 /*
15284  * Based on:
15285  * Ext JS Library 1.1.1
15286  * Copyright(c) 2006-2007, Ext JS, LLC.
15287  *
15288  * Originally Released Under LGPL - original licence link has changed is not relivant.
15289  *
15290  * Fork - LGPL
15291  * <script type="text/javascript">
15292  */
15293 /**
15294  * @class Roo.state.Manager
15295  * This is the global state manager. By default all components that are "state aware" check this class
15296  * for state information if you don't pass them a custom state provider. In order for this class
15297  * to be useful, it must be initialized with a provider when your application initializes.
15298  <pre><code>
15299 // in your initialization function
15300 init : function(){
15301    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15302    ...
15303    // supposed you have a {@link Roo.BorderLayout}
15304    var layout = new Roo.BorderLayout(...);
15305    layout.restoreState();
15306    // or a {Roo.BasicDialog}
15307    var dialog = new Roo.BasicDialog(...);
15308    dialog.restoreState();
15309  </code></pre>
15310  * @singleton
15311  */
15312 Roo.state.Manager = function(){
15313     var provider = new Roo.state.Provider();
15314     
15315     return {
15316         /**
15317          * Configures the default state provider for your application
15318          * @param {Provider} stateProvider The state provider to set
15319          */
15320         setProvider : function(stateProvider){
15321             provider = stateProvider;
15322         },
15323         
15324         /**
15325          * Returns the current value for a key
15326          * @param {String} name The key name
15327          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15328          * @return {Mixed} The state data
15329          */
15330         get : function(key, defaultValue){
15331             return provider.get(key, defaultValue);
15332         },
15333         
15334         /**
15335          * Sets the value for a key
15336          * @param {String} name The key name
15337          * @param {Mixed} value The state data
15338          */
15339          set : function(key, value){
15340             provider.set(key, value);
15341         },
15342         
15343         /**
15344          * Clears a value from the state
15345          * @param {String} name The key name
15346          */
15347         clear : function(key){
15348             provider.clear(key);
15349         },
15350         
15351         /**
15352          * Gets the currently configured state provider
15353          * @return {Provider} The state provider
15354          */
15355         getProvider : function(){
15356             return provider;
15357         }
15358     };
15359 }();
15360 /*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370 /**
15371  * @class Roo.state.CookieProvider
15372  * @extends Roo.state.Provider
15373  * The default Provider implementation which saves state via cookies.
15374  * <br />Usage:
15375  <pre><code>
15376    var cp = new Roo.state.CookieProvider({
15377        path: "/cgi-bin/",
15378        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15379        domain: "roojs.com"
15380    })
15381    Roo.state.Manager.setProvider(cp);
15382  </code></pre>
15383  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15384  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15385  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15386  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15387  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15388  * domain the page is running on including the 'www' like 'www.roojs.com')
15389  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15390  * @constructor
15391  * Create a new CookieProvider
15392  * @param {Object} config The configuration object
15393  */
15394 Roo.state.CookieProvider = function(config){
15395     Roo.state.CookieProvider.superclass.constructor.call(this);
15396     this.path = "/";
15397     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15398     this.domain = null;
15399     this.secure = false;
15400     Roo.apply(this, config);
15401     this.state = this.readCookies();
15402 };
15403
15404 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15405     // private
15406     set : function(name, value){
15407         if(typeof value == "undefined" || value === null){
15408             this.clear(name);
15409             return;
15410         }
15411         this.setCookie(name, value);
15412         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15413     },
15414
15415     // private
15416     clear : function(name){
15417         this.clearCookie(name);
15418         Roo.state.CookieProvider.superclass.clear.call(this, name);
15419     },
15420
15421     // private
15422     readCookies : function(){
15423         var cookies = {};
15424         var c = document.cookie + ";";
15425         var re = /\s?(.*?)=(.*?);/g;
15426         var matches;
15427         while((matches = re.exec(c)) != null){
15428             var name = matches[1];
15429             var value = matches[2];
15430             if(name && name.substring(0,3) == "ys-"){
15431                 cookies[name.substr(3)] = this.decodeValue(value);
15432             }
15433         }
15434         return cookies;
15435     },
15436
15437     // private
15438     setCookie : function(name, value){
15439         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15440            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15441            ((this.path == null) ? "" : ("; path=" + this.path)) +
15442            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15443            ((this.secure == true) ? "; secure" : "");
15444     },
15445
15446     // private
15447     clearCookie : function(name){
15448         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15449            ((this.path == null) ? "" : ("; path=" + this.path)) +
15450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15451            ((this.secure == true) ? "; secure" : "");
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463  
15464
15465 /**
15466  * @class Roo.ComponentMgr
15467  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15468  * @singleton
15469  */
15470 Roo.ComponentMgr = function(){
15471     var all = new Roo.util.MixedCollection();
15472
15473     return {
15474         /**
15475          * Registers a component.
15476          * @param {Roo.Component} c The component
15477          */
15478         register : function(c){
15479             all.add(c);
15480         },
15481
15482         /**
15483          * Unregisters a component.
15484          * @param {Roo.Component} c The component
15485          */
15486         unregister : function(c){
15487             all.remove(c);
15488         },
15489
15490         /**
15491          * Returns a component by id
15492          * @param {String} id The component id
15493          */
15494         get : function(id){
15495             return all.get(id);
15496         },
15497
15498         /**
15499          * Registers a function that will be called when a specified component is added to ComponentMgr
15500          * @param {String} id The component id
15501          * @param {Funtction} fn The callback function
15502          * @param {Object} scope The scope of the callback
15503          */
15504         onAvailable : function(id, fn, scope){
15505             all.on("add", function(index, o){
15506                 if(o.id == id){
15507                     fn.call(scope || o, o);
15508                     all.un("add", fn, scope);
15509                 }
15510             });
15511         }
15512     };
15513 }();/*
15514  * Based on:
15515  * Ext JS Library 1.1.1
15516  * Copyright(c) 2006-2007, Ext JS, LLC.
15517  *
15518  * Originally Released Under LGPL - original licence link has changed is not relivant.
15519  *
15520  * Fork - LGPL
15521  * <script type="text/javascript">
15522  */
15523  
15524 /**
15525  * @class Roo.Component
15526  * @extends Roo.util.Observable
15527  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15528  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15529  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15530  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15531  * All visual components (widgets) that require rendering into a layout should subclass Component.
15532  * @constructor
15533  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15534  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15535  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15536  */
15537 Roo.Component = function(config){
15538     config = config || {};
15539     if(config.tagName || config.dom || typeof config == "string"){ // element object
15540         config = {el: config, id: config.id || config};
15541     }
15542     this.initialConfig = config;
15543
15544     Roo.apply(this, config);
15545     this.addEvents({
15546         /**
15547          * @event disable
15548          * Fires after the component is disabled.
15549              * @param {Roo.Component} this
15550              */
15551         disable : true,
15552         /**
15553          * @event enable
15554          * Fires after the component is enabled.
15555              * @param {Roo.Component} this
15556              */
15557         enable : true,
15558         /**
15559          * @event beforeshow
15560          * Fires before the component is shown.  Return false to stop the show.
15561              * @param {Roo.Component} this
15562              */
15563         beforeshow : true,
15564         /**
15565          * @event show
15566          * Fires after the component is shown.
15567              * @param {Roo.Component} this
15568              */
15569         show : true,
15570         /**
15571          * @event beforehide
15572          * Fires before the component is hidden. Return false to stop the hide.
15573              * @param {Roo.Component} this
15574              */
15575         beforehide : true,
15576         /**
15577          * @event hide
15578          * Fires after the component is hidden.
15579              * @param {Roo.Component} this
15580              */
15581         hide : true,
15582         /**
15583          * @event beforerender
15584          * Fires before the component is rendered. Return false to stop the render.
15585              * @param {Roo.Component} this
15586              */
15587         beforerender : true,
15588         /**
15589          * @event render
15590          * Fires after the component is rendered.
15591              * @param {Roo.Component} this
15592              */
15593         render : true,
15594         /**
15595          * @event beforedestroy
15596          * Fires before the component is destroyed. Return false to stop the destroy.
15597              * @param {Roo.Component} this
15598              */
15599         beforedestroy : true,
15600         /**
15601          * @event destroy
15602          * Fires after the component is destroyed.
15603              * @param {Roo.Component} this
15604              */
15605         destroy : true
15606     });
15607     if(!this.id){
15608         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15609     }
15610     Roo.ComponentMgr.register(this);
15611     Roo.Component.superclass.constructor.call(this);
15612     this.initComponent();
15613     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15614         this.render(this.renderTo);
15615         delete this.renderTo;
15616     }
15617 };
15618
15619 /** @private */
15620 Roo.Component.AUTO_ID = 1000;
15621
15622 Roo.extend(Roo.Component, Roo.util.Observable, {
15623     /**
15624      * @scope Roo.Component.prototype
15625      * @type {Boolean}
15626      * true if this component is hidden. Read-only.
15627      */
15628     hidden : false,
15629     /**
15630      * @type {Boolean}
15631      * true if this component is disabled. Read-only.
15632      */
15633     disabled : false,
15634     /**
15635      * @type {Boolean}
15636      * true if this component has been rendered. Read-only.
15637      */
15638     rendered : false,
15639     
15640     /** @cfg {String} disableClass
15641      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15642      */
15643     disabledClass : "x-item-disabled",
15644         /** @cfg {Boolean} allowDomMove
15645          * Whether the component can move the Dom node when rendering (defaults to true).
15646          */
15647     allowDomMove : true,
15648     /** @cfg {String} hideMode (display|visibility)
15649      * How this component should hidden. Supported values are
15650      * "visibility" (css visibility), "offsets" (negative offset position) and
15651      * "display" (css display) - defaults to "display".
15652      */
15653     hideMode: 'display',
15654
15655     /** @private */
15656     ctype : "Roo.Component",
15657
15658     /**
15659      * @cfg {String} actionMode 
15660      * which property holds the element that used for  hide() / show() / disable() / enable()
15661      * default is 'el' for forms you probably want to set this to fieldEl 
15662      */
15663     actionMode : "el",
15664
15665     /** @private */
15666     getActionEl : function(){
15667         return this[this.actionMode];
15668     },
15669
15670     initComponent : Roo.emptyFn,
15671     /**
15672      * If this is a lazy rendering component, render it to its container element.
15673      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15674      */
15675     render : function(container, position){
15676         
15677         if(this.rendered){
15678             return this;
15679         }
15680         
15681         if(this.fireEvent("beforerender", this) === false){
15682             return false;
15683         }
15684         
15685         if(!container && this.el){
15686             this.el = Roo.get(this.el);
15687             container = this.el.dom.parentNode;
15688             this.allowDomMove = false;
15689         }
15690         this.container = Roo.get(container);
15691         this.rendered = true;
15692         if(position !== undefined){
15693             if(typeof position == 'number'){
15694                 position = this.container.dom.childNodes[position];
15695             }else{
15696                 position = Roo.getDom(position);
15697             }
15698         }
15699         this.onRender(this.container, position || null);
15700         if(this.cls){
15701             this.el.addClass(this.cls);
15702             delete this.cls;
15703         }
15704         if(this.style){
15705             this.el.applyStyles(this.style);
15706             delete this.style;
15707         }
15708         this.fireEvent("render", this);
15709         this.afterRender(this.container);
15710         if(this.hidden){
15711             this.hide();
15712         }
15713         if(this.disabled){
15714             this.disable();
15715         }
15716
15717         return this;
15718         
15719     },
15720
15721     /** @private */
15722     // default function is not really useful
15723     onRender : function(ct, position){
15724         if(this.el){
15725             this.el = Roo.get(this.el);
15726             if(this.allowDomMove !== false){
15727                 ct.dom.insertBefore(this.el.dom, position);
15728             }
15729         }
15730     },
15731
15732     /** @private */
15733     getAutoCreate : function(){
15734         var cfg = typeof this.autoCreate == "object" ?
15735                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15736         if(this.id && !cfg.id){
15737             cfg.id = this.id;
15738         }
15739         return cfg;
15740     },
15741
15742     /** @private */
15743     afterRender : Roo.emptyFn,
15744
15745     /**
15746      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15747      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15748      */
15749     destroy : function(){
15750         if(this.fireEvent("beforedestroy", this) !== false){
15751             this.purgeListeners();
15752             this.beforeDestroy();
15753             if(this.rendered){
15754                 this.el.removeAllListeners();
15755                 this.el.remove();
15756                 if(this.actionMode == "container"){
15757                     this.container.remove();
15758                 }
15759             }
15760             this.onDestroy();
15761             Roo.ComponentMgr.unregister(this);
15762             this.fireEvent("destroy", this);
15763         }
15764     },
15765
15766         /** @private */
15767     beforeDestroy : function(){
15768
15769     },
15770
15771         /** @private */
15772         onDestroy : function(){
15773
15774     },
15775
15776     /**
15777      * Returns the underlying {@link Roo.Element}.
15778      * @return {Roo.Element} The element
15779      */
15780     getEl : function(){
15781         return this.el;
15782     },
15783
15784     /**
15785      * Returns the id of this component.
15786      * @return {String}
15787      */
15788     getId : function(){
15789         return this.id;
15790     },
15791
15792     /**
15793      * Try to focus this component.
15794      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15795      * @return {Roo.Component} this
15796      */
15797     focus : function(selectText){
15798         if(this.rendered){
15799             this.el.focus();
15800             if(selectText === true){
15801                 this.el.dom.select();
15802             }
15803         }
15804         return this;
15805     },
15806
15807     /** @private */
15808     blur : function(){
15809         if(this.rendered){
15810             this.el.blur();
15811         }
15812         return this;
15813     },
15814
15815     /**
15816      * Disable this component.
15817      * @return {Roo.Component} this
15818      */
15819     disable : function(){
15820         if(this.rendered){
15821             this.onDisable();
15822         }
15823         this.disabled = true;
15824         this.fireEvent("disable", this);
15825         return this;
15826     },
15827
15828         // private
15829     onDisable : function(){
15830         this.getActionEl().addClass(this.disabledClass);
15831         this.el.dom.disabled = true;
15832     },
15833
15834     /**
15835      * Enable this component.
15836      * @return {Roo.Component} this
15837      */
15838     enable : function(){
15839         if(this.rendered){
15840             this.onEnable();
15841         }
15842         this.disabled = false;
15843         this.fireEvent("enable", this);
15844         return this;
15845     },
15846
15847         // private
15848     onEnable : function(){
15849         this.getActionEl().removeClass(this.disabledClass);
15850         this.el.dom.disabled = false;
15851     },
15852
15853     /**
15854      * Convenience function for setting disabled/enabled by boolean.
15855      * @param {Boolean} disabled
15856      */
15857     setDisabled : function(disabled){
15858         this[disabled ? "disable" : "enable"]();
15859     },
15860
15861     /**
15862      * Show this component.
15863      * @return {Roo.Component} this
15864      */
15865     show: function(){
15866         if(this.fireEvent("beforeshow", this) !== false){
15867             this.hidden = false;
15868             if(this.rendered){
15869                 this.onShow();
15870             }
15871             this.fireEvent("show", this);
15872         }
15873         return this;
15874     },
15875
15876     // private
15877     onShow : function(){
15878         var ae = this.getActionEl();
15879         if(this.hideMode == 'visibility'){
15880             ae.dom.style.visibility = "visible";
15881         }else if(this.hideMode == 'offsets'){
15882             ae.removeClass('x-hidden');
15883         }else{
15884             ae.dom.style.display = "";
15885         }
15886     },
15887
15888     /**
15889      * Hide this component.
15890      * @return {Roo.Component} this
15891      */
15892     hide: function(){
15893         if(this.fireEvent("beforehide", this) !== false){
15894             this.hidden = true;
15895             if(this.rendered){
15896                 this.onHide();
15897             }
15898             this.fireEvent("hide", this);
15899         }
15900         return this;
15901     },
15902
15903     // private
15904     onHide : function(){
15905         var ae = this.getActionEl();
15906         if(this.hideMode == 'visibility'){
15907             ae.dom.style.visibility = "hidden";
15908         }else if(this.hideMode == 'offsets'){
15909             ae.addClass('x-hidden');
15910         }else{
15911             ae.dom.style.display = "none";
15912         }
15913     },
15914
15915     /**
15916      * Convenience function to hide or show this component by boolean.
15917      * @param {Boolean} visible True to show, false to hide
15918      * @return {Roo.Component} this
15919      */
15920     setVisible: function(visible){
15921         if(visible) {
15922             this.show();
15923         }else{
15924             this.hide();
15925         }
15926         return this;
15927     },
15928
15929     /**
15930      * Returns true if this component is visible.
15931      */
15932     isVisible : function(){
15933         return this.getActionEl().isVisible();
15934     },
15935
15936     cloneConfig : function(overrides){
15937         overrides = overrides || {};
15938         var id = overrides.id || Roo.id();
15939         var cfg = Roo.applyIf(overrides, this.initialConfig);
15940         cfg.id = id; // prevent dup id
15941         return new this.constructor(cfg);
15942     }
15943 });/*
15944  * Based on:
15945  * Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  *
15948  * Originally Released Under LGPL - original licence link has changed is not relivant.
15949  *
15950  * Fork - LGPL
15951  * <script type="text/javascript">
15952  */
15953
15954 /**
15955  * @class Roo.BoxComponent
15956  * @extends Roo.Component
15957  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15958  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15959  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15960  * layout containers.
15961  * @constructor
15962  * @param {Roo.Element/String/Object} config The configuration options.
15963  */
15964 Roo.BoxComponent = function(config){
15965     Roo.Component.call(this, config);
15966     this.addEvents({
15967         /**
15968          * @event resize
15969          * Fires after the component is resized.
15970              * @param {Roo.Component} this
15971              * @param {Number} adjWidth The box-adjusted width that was set
15972              * @param {Number} adjHeight The box-adjusted height that was set
15973              * @param {Number} rawWidth The width that was originally specified
15974              * @param {Number} rawHeight The height that was originally specified
15975              */
15976         resize : true,
15977         /**
15978          * @event move
15979          * Fires after the component is moved.
15980              * @param {Roo.Component} this
15981              * @param {Number} x The new x position
15982              * @param {Number} y The new y position
15983              */
15984         move : true
15985     });
15986 };
15987
15988 Roo.extend(Roo.BoxComponent, Roo.Component, {
15989     // private, set in afterRender to signify that the component has been rendered
15990     boxReady : false,
15991     // private, used to defer height settings to subclasses
15992     deferHeight: false,
15993     /** @cfg {Number} width
15994      * width (optional) size of component
15995      */
15996      /** @cfg {Number} height
15997      * height (optional) size of component
15998      */
15999      
16000     /**
16001      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16002      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16003      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16004      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16005      * @return {Roo.BoxComponent} this
16006      */
16007     setSize : function(w, h){
16008         // support for standard size objects
16009         if(typeof w == 'object'){
16010             h = w.height;
16011             w = w.width;
16012         }
16013         // not rendered
16014         if(!this.boxReady){
16015             this.width = w;
16016             this.height = h;
16017             return this;
16018         }
16019
16020         // prevent recalcs when not needed
16021         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16022             return this;
16023         }
16024         this.lastSize = {width: w, height: h};
16025
16026         var adj = this.adjustSize(w, h);
16027         var aw = adj.width, ah = adj.height;
16028         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16029             var rz = this.getResizeEl();
16030             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16031                 rz.setSize(aw, ah);
16032             }else if(!this.deferHeight && ah !== undefined){
16033                 rz.setHeight(ah);
16034             }else if(aw !== undefined){
16035                 rz.setWidth(aw);
16036             }
16037             this.onResize(aw, ah, w, h);
16038             this.fireEvent('resize', this, aw, ah, w, h);
16039         }
16040         return this;
16041     },
16042
16043     /**
16044      * Gets the current size of the component's underlying element.
16045      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16046      */
16047     getSize : function(){
16048         return this.el.getSize();
16049     },
16050
16051     /**
16052      * Gets the current XY position of the component's underlying element.
16053      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16054      * @return {Array} The XY position of the element (e.g., [100, 200])
16055      */
16056     getPosition : function(local){
16057         if(local === true){
16058             return [this.el.getLeft(true), this.el.getTop(true)];
16059         }
16060         return this.xy || this.el.getXY();
16061     },
16062
16063     /**
16064      * Gets the current box measurements of the component's underlying element.
16065      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16066      * @returns {Object} box An object in the format {x, y, width, height}
16067      */
16068     getBox : function(local){
16069         var s = this.el.getSize();
16070         if(local){
16071             s.x = this.el.getLeft(true);
16072             s.y = this.el.getTop(true);
16073         }else{
16074             var xy = this.xy || this.el.getXY();
16075             s.x = xy[0];
16076             s.y = xy[1];
16077         }
16078         return s;
16079     },
16080
16081     /**
16082      * Sets the current box measurements of the component's underlying element.
16083      * @param {Object} box An object in the format {x, y, width, height}
16084      * @returns {Roo.BoxComponent} this
16085      */
16086     updateBox : function(box){
16087         this.setSize(box.width, box.height);
16088         this.setPagePosition(box.x, box.y);
16089         return this;
16090     },
16091
16092     // protected
16093     getResizeEl : function(){
16094         return this.resizeEl || this.el;
16095     },
16096
16097     // protected
16098     getPositionEl : function(){
16099         return this.positionEl || this.el;
16100     },
16101
16102     /**
16103      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16104      * This method fires the move event.
16105      * @param {Number} left The new left
16106      * @param {Number} top The new top
16107      * @returns {Roo.BoxComponent} this
16108      */
16109     setPosition : function(x, y){
16110         this.x = x;
16111         this.y = y;
16112         if(!this.boxReady){
16113             return this;
16114         }
16115         var adj = this.adjustPosition(x, y);
16116         var ax = adj.x, ay = adj.y;
16117
16118         var el = this.getPositionEl();
16119         if(ax !== undefined || ay !== undefined){
16120             if(ax !== undefined && ay !== undefined){
16121                 el.setLeftTop(ax, ay);
16122             }else if(ax !== undefined){
16123                 el.setLeft(ax);
16124             }else if(ay !== undefined){
16125                 el.setTop(ay);
16126             }
16127             this.onPosition(ax, ay);
16128             this.fireEvent('move', this, ax, ay);
16129         }
16130         return this;
16131     },
16132
16133     /**
16134      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16135      * This method fires the move event.
16136      * @param {Number} x The new x position
16137      * @param {Number} y The new y position
16138      * @returns {Roo.BoxComponent} this
16139      */
16140     setPagePosition : function(x, y){
16141         this.pageX = x;
16142         this.pageY = y;
16143         if(!this.boxReady){
16144             return;
16145         }
16146         if(x === undefined || y === undefined){ // cannot translate undefined points
16147             return;
16148         }
16149         var p = this.el.translatePoints(x, y);
16150         this.setPosition(p.left, p.top);
16151         return this;
16152     },
16153
16154     // private
16155     onRender : function(ct, position){
16156         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16157         if(this.resizeEl){
16158             this.resizeEl = Roo.get(this.resizeEl);
16159         }
16160         if(this.positionEl){
16161             this.positionEl = Roo.get(this.positionEl);
16162         }
16163     },
16164
16165     // private
16166     afterRender : function(){
16167         Roo.BoxComponent.superclass.afterRender.call(this);
16168         this.boxReady = true;
16169         this.setSize(this.width, this.height);
16170         if(this.x || this.y){
16171             this.setPosition(this.x, this.y);
16172         }
16173         if(this.pageX || this.pageY){
16174             this.setPagePosition(this.pageX, this.pageY);
16175         }
16176     },
16177
16178     /**
16179      * Force the component's size to recalculate based on the underlying element's current height and width.
16180      * @returns {Roo.BoxComponent} this
16181      */
16182     syncSize : function(){
16183         delete this.lastSize;
16184         this.setSize(this.el.getWidth(), this.el.getHeight());
16185         return this;
16186     },
16187
16188     /**
16189      * Called after the component is resized, this method is empty by default but can be implemented by any
16190      * subclass that needs to perform custom logic after a resize occurs.
16191      * @param {Number} adjWidth The box-adjusted width that was set
16192      * @param {Number} adjHeight The box-adjusted height that was set
16193      * @param {Number} rawWidth The width that was originally specified
16194      * @param {Number} rawHeight The height that was originally specified
16195      */
16196     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16197
16198     },
16199
16200     /**
16201      * Called after the component is moved, this method is empty by default but can be implemented by any
16202      * subclass that needs to perform custom logic after a move occurs.
16203      * @param {Number} x The new x position
16204      * @param {Number} y The new y position
16205      */
16206     onPosition : function(x, y){
16207
16208     },
16209
16210     // private
16211     adjustSize : function(w, h){
16212         if(this.autoWidth){
16213             w = 'auto';
16214         }
16215         if(this.autoHeight){
16216             h = 'auto';
16217         }
16218         return {width : w, height: h};
16219     },
16220
16221     // private
16222     adjustPosition : function(x, y){
16223         return {x : x, y: y};
16224     }
16225 });/*
16226  * Based on:
16227  * Ext JS Library 1.1.1
16228  * Copyright(c) 2006-2007, Ext JS, LLC.
16229  *
16230  * Originally Released Under LGPL - original licence link has changed is not relivant.
16231  *
16232  * Fork - LGPL
16233  * <script type="text/javascript">
16234  */
16235  (function(){ 
16236 /**
16237  * @class Roo.Layer
16238  * @extends Roo.Element
16239  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16240  * automatic maintaining of shadow/shim positions.
16241  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16242  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16243  * you can pass a string with a CSS class name. False turns off the shadow.
16244  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16245  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16246  * @cfg {String} cls CSS class to add to the element
16247  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16248  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16249  * @constructor
16250  * @param {Object} config An object with config options.
16251  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16252  */
16253
16254 Roo.Layer = function(config, existingEl){
16255     config = config || {};
16256     var dh = Roo.DomHelper;
16257     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16258     if(existingEl){
16259         this.dom = Roo.getDom(existingEl);
16260     }
16261     if(!this.dom){
16262         var o = config.dh || {tag: "div", cls: "x-layer"};
16263         this.dom = dh.append(pel, o);
16264     }
16265     if(config.cls){
16266         this.addClass(config.cls);
16267     }
16268     this.constrain = config.constrain !== false;
16269     this.visibilityMode = Roo.Element.VISIBILITY;
16270     if(config.id){
16271         this.id = this.dom.id = config.id;
16272     }else{
16273         this.id = Roo.id(this.dom);
16274     }
16275     this.zindex = config.zindex || this.getZIndex();
16276     this.position("absolute", this.zindex);
16277     if(config.shadow){
16278         this.shadowOffset = config.shadowOffset || 4;
16279         this.shadow = new Roo.Shadow({
16280             offset : this.shadowOffset,
16281             mode : config.shadow
16282         });
16283     }else{
16284         this.shadowOffset = 0;
16285     }
16286     this.useShim = config.shim !== false && Roo.useShims;
16287     this.useDisplay = config.useDisplay;
16288     this.hide();
16289 };
16290
16291 var supr = Roo.Element.prototype;
16292
16293 // shims are shared among layer to keep from having 100 iframes
16294 var shims = [];
16295
16296 Roo.extend(Roo.Layer, Roo.Element, {
16297
16298     getZIndex : function(){
16299         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16300     },
16301
16302     getShim : function(){
16303         if(!this.useShim){
16304             return null;
16305         }
16306         if(this.shim){
16307             return this.shim;
16308         }
16309         var shim = shims.shift();
16310         if(!shim){
16311             shim = this.createShim();
16312             shim.enableDisplayMode('block');
16313             shim.dom.style.display = 'none';
16314             shim.dom.style.visibility = 'visible';
16315         }
16316         var pn = this.dom.parentNode;
16317         if(shim.dom.parentNode != pn){
16318             pn.insertBefore(shim.dom, this.dom);
16319         }
16320         shim.setStyle('z-index', this.getZIndex()-2);
16321         this.shim = shim;
16322         return shim;
16323     },
16324
16325     hideShim : function(){
16326         if(this.shim){
16327             this.shim.setDisplayed(false);
16328             shims.push(this.shim);
16329             delete this.shim;
16330         }
16331     },
16332
16333     disableShadow : function(){
16334         if(this.shadow){
16335             this.shadowDisabled = true;
16336             this.shadow.hide();
16337             this.lastShadowOffset = this.shadowOffset;
16338             this.shadowOffset = 0;
16339         }
16340     },
16341
16342     enableShadow : function(show){
16343         if(this.shadow){
16344             this.shadowDisabled = false;
16345             this.shadowOffset = this.lastShadowOffset;
16346             delete this.lastShadowOffset;
16347             if(show){
16348                 this.sync(true);
16349             }
16350         }
16351     },
16352
16353     // private
16354     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16355     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16356     sync : function(doShow){
16357         var sw = this.shadow;
16358         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16359             var sh = this.getShim();
16360
16361             var w = this.getWidth(),
16362                 h = this.getHeight();
16363
16364             var l = this.getLeft(true),
16365                 t = this.getTop(true);
16366
16367             if(sw && !this.shadowDisabled){
16368                 if(doShow && !sw.isVisible()){
16369                     sw.show(this);
16370                 }else{
16371                     sw.realign(l, t, w, h);
16372                 }
16373                 if(sh){
16374                     if(doShow){
16375                        sh.show();
16376                     }
16377                     // fit the shim behind the shadow, so it is shimmed too
16378                     var a = sw.adjusts, s = sh.dom.style;
16379                     s.left = (Math.min(l, l+a.l))+"px";
16380                     s.top = (Math.min(t, t+a.t))+"px";
16381                     s.width = (w+a.w)+"px";
16382                     s.height = (h+a.h)+"px";
16383                 }
16384             }else if(sh){
16385                 if(doShow){
16386                    sh.show();
16387                 }
16388                 sh.setSize(w, h);
16389                 sh.setLeftTop(l, t);
16390             }
16391             
16392         }
16393     },
16394
16395     // private
16396     destroy : function(){
16397         this.hideShim();
16398         if(this.shadow){
16399             this.shadow.hide();
16400         }
16401         this.removeAllListeners();
16402         var pn = this.dom.parentNode;
16403         if(pn){
16404             pn.removeChild(this.dom);
16405         }
16406         Roo.Element.uncache(this.id);
16407     },
16408
16409     remove : function(){
16410         this.destroy();
16411     },
16412
16413     // private
16414     beginUpdate : function(){
16415         this.updating = true;
16416     },
16417
16418     // private
16419     endUpdate : function(){
16420         this.updating = false;
16421         this.sync(true);
16422     },
16423
16424     // private
16425     hideUnders : function(negOffset){
16426         if(this.shadow){
16427             this.shadow.hide();
16428         }
16429         this.hideShim();
16430     },
16431
16432     // private
16433     constrainXY : function(){
16434         if(this.constrain){
16435             var vw = Roo.lib.Dom.getViewWidth(),
16436                 vh = Roo.lib.Dom.getViewHeight();
16437             var s = Roo.get(document).getScroll();
16438
16439             var xy = this.getXY();
16440             var x = xy[0], y = xy[1];   
16441             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16442             // only move it if it needs it
16443             var moved = false;
16444             // first validate right/bottom
16445             if((x + w) > vw+s.left){
16446                 x = vw - w - this.shadowOffset;
16447                 moved = true;
16448             }
16449             if((y + h) > vh+s.top){
16450                 y = vh - h - this.shadowOffset;
16451                 moved = true;
16452             }
16453             // then make sure top/left isn't negative
16454             if(x < s.left){
16455                 x = s.left;
16456                 moved = true;
16457             }
16458             if(y < s.top){
16459                 y = s.top;
16460                 moved = true;
16461             }
16462             if(moved){
16463                 if(this.avoidY){
16464                     var ay = this.avoidY;
16465                     if(y <= ay && (y+h) >= ay){
16466                         y = ay-h-5;   
16467                     }
16468                 }
16469                 xy = [x, y];
16470                 this.storeXY(xy);
16471                 supr.setXY.call(this, xy);
16472                 this.sync();
16473             }
16474         }
16475     },
16476
16477     isVisible : function(){
16478         return this.visible;    
16479     },
16480
16481     // private
16482     showAction : function(){
16483         this.visible = true; // track visibility to prevent getStyle calls
16484         if(this.useDisplay === true){
16485             this.setDisplayed("");
16486         }else if(this.lastXY){
16487             supr.setXY.call(this, this.lastXY);
16488         }else if(this.lastLT){
16489             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16490         }
16491     },
16492
16493     // private
16494     hideAction : function(){
16495         this.visible = false;
16496         if(this.useDisplay === true){
16497             this.setDisplayed(false);
16498         }else{
16499             this.setLeftTop(-10000,-10000);
16500         }
16501     },
16502
16503     // overridden Element method
16504     setVisible : function(v, a, d, c, e){
16505         if(v){
16506             this.showAction();
16507         }
16508         if(a && v){
16509             var cb = function(){
16510                 this.sync(true);
16511                 if(c){
16512                     c();
16513                 }
16514             }.createDelegate(this);
16515             supr.setVisible.call(this, true, true, d, cb, e);
16516         }else{
16517             if(!v){
16518                 this.hideUnders(true);
16519             }
16520             var cb = c;
16521             if(a){
16522                 cb = function(){
16523                     this.hideAction();
16524                     if(c){
16525                         c();
16526                     }
16527                 }.createDelegate(this);
16528             }
16529             supr.setVisible.call(this, v, a, d, cb, e);
16530             if(v){
16531                 this.sync(true);
16532             }else if(!a){
16533                 this.hideAction();
16534             }
16535         }
16536     },
16537
16538     storeXY : function(xy){
16539         delete this.lastLT;
16540         this.lastXY = xy;
16541     },
16542
16543     storeLeftTop : function(left, top){
16544         delete this.lastXY;
16545         this.lastLT = [left, top];
16546     },
16547
16548     // private
16549     beforeFx : function(){
16550         this.beforeAction();
16551         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16552     },
16553
16554     // private
16555     afterFx : function(){
16556         Roo.Layer.superclass.afterFx.apply(this, arguments);
16557         this.sync(this.isVisible());
16558     },
16559
16560     // private
16561     beforeAction : function(){
16562         if(!this.updating && this.shadow){
16563             this.shadow.hide();
16564         }
16565     },
16566
16567     // overridden Element method
16568     setLeft : function(left){
16569         this.storeLeftTop(left, this.getTop(true));
16570         supr.setLeft.apply(this, arguments);
16571         this.sync();
16572     },
16573
16574     setTop : function(top){
16575         this.storeLeftTop(this.getLeft(true), top);
16576         supr.setTop.apply(this, arguments);
16577         this.sync();
16578     },
16579
16580     setLeftTop : function(left, top){
16581         this.storeLeftTop(left, top);
16582         supr.setLeftTop.apply(this, arguments);
16583         this.sync();
16584     },
16585
16586     setXY : function(xy, a, d, c, e){
16587         this.fixDisplay();
16588         this.beforeAction();
16589         this.storeXY(xy);
16590         var cb = this.createCB(c);
16591         supr.setXY.call(this, xy, a, d, cb, e);
16592         if(!a){
16593             cb();
16594         }
16595     },
16596
16597     // private
16598     createCB : function(c){
16599         var el = this;
16600         return function(){
16601             el.constrainXY();
16602             el.sync(true);
16603             if(c){
16604                 c();
16605             }
16606         };
16607     },
16608
16609     // overridden Element method
16610     setX : function(x, a, d, c, e){
16611         this.setXY([x, this.getY()], a, d, c, e);
16612     },
16613
16614     // overridden Element method
16615     setY : function(y, a, d, c, e){
16616         this.setXY([this.getX(), y], a, d, c, e);
16617     },
16618
16619     // overridden Element method
16620     setSize : function(w, h, a, d, c, e){
16621         this.beforeAction();
16622         var cb = this.createCB(c);
16623         supr.setSize.call(this, w, h, a, d, cb, e);
16624         if(!a){
16625             cb();
16626         }
16627     },
16628
16629     // overridden Element method
16630     setWidth : function(w, a, d, c, e){
16631         this.beforeAction();
16632         var cb = this.createCB(c);
16633         supr.setWidth.call(this, w, a, d, cb, e);
16634         if(!a){
16635             cb();
16636         }
16637     },
16638
16639     // overridden Element method
16640     setHeight : function(h, a, d, c, e){
16641         this.beforeAction();
16642         var cb = this.createCB(c);
16643         supr.setHeight.call(this, h, a, d, cb, e);
16644         if(!a){
16645             cb();
16646         }
16647     },
16648
16649     // overridden Element method
16650     setBounds : function(x, y, w, h, a, d, c, e){
16651         this.beforeAction();
16652         var cb = this.createCB(c);
16653         if(!a){
16654             this.storeXY([x, y]);
16655             supr.setXY.call(this, [x, y]);
16656             supr.setSize.call(this, w, h, a, d, cb, e);
16657             cb();
16658         }else{
16659             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16660         }
16661         return this;
16662     },
16663     
16664     /**
16665      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16666      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16667      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16668      * @param {Number} zindex The new z-index to set
16669      * @return {this} The Layer
16670      */
16671     setZIndex : function(zindex){
16672         this.zindex = zindex;
16673         this.setStyle("z-index", zindex + 2);
16674         if(this.shadow){
16675             this.shadow.setZIndex(zindex + 1);
16676         }
16677         if(this.shim){
16678             this.shim.setStyle("z-index", zindex);
16679         }
16680     }
16681 });
16682 })();/*
16683  * Original code for Roojs - LGPL
16684  * <script type="text/javascript">
16685  */
16686  
16687 /**
16688  * @class Roo.XComponent
16689  * A delayed Element creator...
16690  * Or a way to group chunks of interface together.
16691  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16692  *  used in conjunction with XComponent.build() it will create an instance of each element,
16693  *  then call addxtype() to build the User interface.
16694  * 
16695  * Mypart.xyx = new Roo.XComponent({
16696
16697     parent : 'Mypart.xyz', // empty == document.element.!!
16698     order : '001',
16699     name : 'xxxx'
16700     region : 'xxxx'
16701     disabled : function() {} 
16702      
16703     tree : function() { // return an tree of xtype declared components
16704         var MODULE = this;
16705         return 
16706         {
16707             xtype : 'NestedLayoutPanel',
16708             // technicall
16709         }
16710      ]
16711  *})
16712  *
16713  *
16714  * It can be used to build a big heiracy, with parent etc.
16715  * or you can just use this to render a single compoent to a dom element
16716  * MYPART.render(Roo.Element | String(id) | dom_element )
16717  *
16718  *
16719  * Usage patterns.
16720  *
16721  * Classic Roo
16722  *
16723  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16724  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16725  *
16726  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16727  *
16728  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16729  * - if mulitple topModules exist, the last one is defined as the top module.
16730  *
16731  * Embeded Roo
16732  * 
16733  * When the top level or multiple modules are to embedded into a existing HTML page,
16734  * the parent element can container '#id' of the element where the module will be drawn.
16735  *
16736  * Bootstrap Roo
16737  *
16738  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16739  * it relies more on a include mechanism, where sub modules are included into an outer page.
16740  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16741  * 
16742  * Bootstrap Roo Included elements
16743  *
16744  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16745  * hence confusing the component builder as it thinks there are multiple top level elements. 
16746  *
16747  * String Over-ride & Translations
16748  *
16749  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16750  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16751  * are needed. @see Roo.XComponent.overlayString  
16752  * 
16753  * 
16754  * 
16755  * @extends Roo.util.Observable
16756  * @constructor
16757  * @param cfg {Object} configuration of component
16758  * 
16759  */
16760 Roo.XComponent = function(cfg) {
16761     Roo.apply(this, cfg);
16762     this.addEvents({ 
16763         /**
16764              * @event built
16765              * Fires when this the componnt is built
16766              * @param {Roo.XComponent} c the component
16767              */
16768         'built' : true
16769         
16770     });
16771     this.region = this.region || 'center'; // default..
16772     Roo.XComponent.register(this);
16773     this.modules = false;
16774     this.el = false; // where the layout goes..
16775     
16776     
16777 }
16778 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16779     /**
16780      * @property el
16781      * The created element (with Roo.factory())
16782      * @type {Roo.Layout}
16783      */
16784     el  : false,
16785     
16786     /**
16787      * @property el
16788      * for BC  - use el in new code
16789      * @type {Roo.Layout}
16790      */
16791     panel : false,
16792     
16793     /**
16794      * @property layout
16795      * for BC  - use el in new code
16796      * @type {Roo.Layout}
16797      */
16798     layout : false,
16799     
16800      /**
16801      * @cfg {Function|boolean} disabled
16802      * If this module is disabled by some rule, return true from the funtion
16803      */
16804     disabled : false,
16805     
16806     /**
16807      * @cfg {String} parent 
16808      * Name of parent element which it get xtype added to..
16809      */
16810     parent: false,
16811     
16812     /**
16813      * @cfg {String} order
16814      * Used to set the order in which elements are created (usefull for multiple tabs)
16815      */
16816     
16817     order : false,
16818     /**
16819      * @cfg {String} name
16820      * String to display while loading.
16821      */
16822     name : false,
16823     /**
16824      * @cfg {String} region
16825      * Region to render component to (defaults to center)
16826      */
16827     region : 'center',
16828     
16829     /**
16830      * @cfg {Array} items
16831      * A single item array - the first element is the root of the tree..
16832      * It's done this way to stay compatible with the Xtype system...
16833      */
16834     items : false,
16835     
16836     /**
16837      * @property _tree
16838      * The method that retuns the tree of parts that make up this compoennt 
16839      * @type {function}
16840      */
16841     _tree  : false,
16842     
16843      /**
16844      * render
16845      * render element to dom or tree
16846      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16847      */
16848     
16849     render : function(el)
16850     {
16851         
16852         el = el || false;
16853         var hp = this.parent ? 1 : 0;
16854         Roo.debug &&  Roo.log(this);
16855         
16856         var tree = this._tree ? this._tree() : this.tree();
16857
16858         
16859         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16860             // if parent is a '#.....' string, then let's use that..
16861             var ename = this.parent.substr(1);
16862             this.parent = false;
16863             Roo.debug && Roo.log(ename);
16864             switch (ename) {
16865                 case 'bootstrap-body':
16866                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16867                         // this is the BorderLayout standard?
16868                        this.parent = { el : true };
16869                        break;
16870                     }
16871                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16872                         // need to insert stuff...
16873                         this.parent =  {
16874                              el : new Roo.bootstrap.layout.Border({
16875                                  el : document.body, 
16876                      
16877                                  center: {
16878                                     titlebar: false,
16879                                     autoScroll:false,
16880                                     closeOnTab: true,
16881                                     tabPosition: 'top',
16882                                       //resizeTabs: true,
16883                                     alwaysShowTabs: true,
16884                                     hideTabs: false
16885                                      //minTabWidth: 140
16886                                  }
16887                              })
16888                         
16889                          };
16890                          break;
16891                     }
16892                          
16893                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16894                         this.parent = { el :  new  Roo.bootstrap.Body() };
16895                         Roo.debug && Roo.log("setting el to doc body");
16896                          
16897                     } else {
16898                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16899                     }
16900                     break;
16901                 case 'bootstrap':
16902                     this.parent = { el : true};
16903                     // fall through
16904                 default:
16905                     el = Roo.get(ename);
16906                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16907                         this.parent = { el : true};
16908                     }
16909                     
16910                     break;
16911             }
16912                 
16913             
16914             if (!el && !this.parent) {
16915                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16916                 return;
16917             }
16918         }
16919         
16920         Roo.debug && Roo.log("EL:");
16921         Roo.debug && Roo.log(el);
16922         Roo.debug && Roo.log("this.parent.el:");
16923         Roo.debug && Roo.log(this.parent.el);
16924         
16925
16926         // altertive root elements ??? - we need a better way to indicate these.
16927         var is_alt = Roo.XComponent.is_alt ||
16928                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16929                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16930                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16931         
16932         
16933         
16934         if (!this.parent && is_alt) {
16935             //el = Roo.get(document.body);
16936             this.parent = { el : true };
16937         }
16938             
16939             
16940         
16941         if (!this.parent) {
16942             
16943             Roo.debug && Roo.log("no parent - creating one");
16944             
16945             el = el ? Roo.get(el) : false;      
16946             
16947             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16948                 
16949                 this.parent =  {
16950                     el : new Roo.bootstrap.layout.Border({
16951                         el: el || document.body,
16952                     
16953                         center: {
16954                             titlebar: false,
16955                             autoScroll:false,
16956                             closeOnTab: true,
16957                             tabPosition: 'top',
16958                              //resizeTabs: true,
16959                             alwaysShowTabs: false,
16960                             hideTabs: true,
16961                             minTabWidth: 140,
16962                             overflow: 'visible'
16963                          }
16964                      })
16965                 };
16966             } else {
16967             
16968                 // it's a top level one..
16969                 this.parent =  {
16970                     el : new Roo.BorderLayout(el || document.body, {
16971                         center: {
16972                             titlebar: false,
16973                             autoScroll:false,
16974                             closeOnTab: true,
16975                             tabPosition: 'top',
16976                              //resizeTabs: true,
16977                             alwaysShowTabs: el && hp? false :  true,
16978                             hideTabs: el || !hp ? true :  false,
16979                             minTabWidth: 140
16980                          }
16981                     })
16982                 };
16983             }
16984         }
16985         
16986         if (!this.parent.el) {
16987                 // probably an old style ctor, which has been disabled.
16988                 return;
16989
16990         }
16991                 // The 'tree' method is  '_tree now' 
16992             
16993         tree.region = tree.region || this.region;
16994         var is_body = false;
16995         if (this.parent.el === true) {
16996             // bootstrap... - body..
16997             if (el) {
16998                 tree.el = el;
16999             }
17000             this.parent.el = Roo.factory(tree);
17001             is_body = true;
17002         }
17003         
17004         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17005         this.fireEvent('built', this);
17006         
17007         this.panel = this.el;
17008         this.layout = this.panel.layout;
17009         this.parentLayout = this.parent.layout  || false;  
17010          
17011     }
17012     
17013 });
17014
17015 Roo.apply(Roo.XComponent, {
17016     /**
17017      * @property  hideProgress
17018      * true to disable the building progress bar.. usefull on single page renders.
17019      * @type Boolean
17020      */
17021     hideProgress : false,
17022     /**
17023      * @property  buildCompleted
17024      * True when the builder has completed building the interface.
17025      * @type Boolean
17026      */
17027     buildCompleted : false,
17028      
17029     /**
17030      * @property  topModule
17031      * the upper most module - uses document.element as it's constructor.
17032      * @type Object
17033      */
17034      
17035     topModule  : false,
17036       
17037     /**
17038      * @property  modules
17039      * array of modules to be created by registration system.
17040      * @type {Array} of Roo.XComponent
17041      */
17042     
17043     modules : [],
17044     /**
17045      * @property  elmodules
17046      * array of modules to be created by which use #ID 
17047      * @type {Array} of Roo.XComponent
17048      */
17049      
17050     elmodules : [],
17051
17052      /**
17053      * @property  is_alt
17054      * Is an alternative Root - normally used by bootstrap or other systems,
17055      *    where the top element in the tree can wrap 'body' 
17056      * @type {boolean}  (default false)
17057      */
17058      
17059     is_alt : false,
17060     /**
17061      * @property  build_from_html
17062      * Build elements from html - used by bootstrap HTML stuff 
17063      *    - this is cleared after build is completed
17064      * @type {boolean}    (default false)
17065      */
17066      
17067     build_from_html : false,
17068     /**
17069      * Register components to be built later.
17070      *
17071      * This solves the following issues
17072      * - Building is not done on page load, but after an authentication process has occured.
17073      * - Interface elements are registered on page load
17074      * - Parent Interface elements may not be loaded before child, so this handles that..
17075      * 
17076      *
17077      * example:
17078      * 
17079      * MyApp.register({
17080           order : '000001',
17081           module : 'Pman.Tab.projectMgr',
17082           region : 'center',
17083           parent : 'Pman.layout',
17084           disabled : false,  // or use a function..
17085         })
17086      
17087      * * @param {Object} details about module
17088      */
17089     register : function(obj) {
17090                 
17091         Roo.XComponent.event.fireEvent('register', obj);
17092         switch(typeof(obj.disabled) ) {
17093                 
17094             case 'undefined':
17095                 break;
17096             
17097             case 'function':
17098                 if ( obj.disabled() ) {
17099                         return;
17100                 }
17101                 break;
17102             
17103             default:
17104                 if (obj.disabled || obj.region == '#disabled') {
17105                         return;
17106                 }
17107                 break;
17108         }
17109                 
17110         this.modules.push(obj);
17111          
17112     },
17113     /**
17114      * convert a string to an object..
17115      * eg. 'AAA.BBB' -> finds AAA.BBB
17116
17117      */
17118     
17119     toObject : function(str)
17120     {
17121         if (!str || typeof(str) == 'object') {
17122             return str;
17123         }
17124         if (str.substring(0,1) == '#') {
17125             return str;
17126         }
17127
17128         var ar = str.split('.');
17129         var rt, o;
17130         rt = ar.shift();
17131             /** eval:var:o */
17132         try {
17133             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17134         } catch (e) {
17135             throw "Module not found : " + str;
17136         }
17137         
17138         if (o === false) {
17139             throw "Module not found : " + str;
17140         }
17141         Roo.each(ar, function(e) {
17142             if (typeof(o[e]) == 'undefined') {
17143                 throw "Module not found : " + str;
17144             }
17145             o = o[e];
17146         });
17147         
17148         return o;
17149         
17150     },
17151     
17152     
17153     /**
17154      * move modules into their correct place in the tree..
17155      * 
17156      */
17157     preBuild : function ()
17158     {
17159         var _t = this;
17160         Roo.each(this.modules , function (obj)
17161         {
17162             Roo.XComponent.event.fireEvent('beforebuild', obj);
17163             
17164             var opar = obj.parent;
17165             try { 
17166                 obj.parent = this.toObject(opar);
17167             } catch(e) {
17168                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17169                 return;
17170             }
17171             
17172             if (!obj.parent) {
17173                 Roo.debug && Roo.log("GOT top level module");
17174                 Roo.debug && Roo.log(obj);
17175                 obj.modules = new Roo.util.MixedCollection(false, 
17176                     function(o) { return o.order + '' }
17177                 );
17178                 this.topModule = obj;
17179                 return;
17180             }
17181                         // parent is a string (usually a dom element name..)
17182             if (typeof(obj.parent) == 'string') {
17183                 this.elmodules.push(obj);
17184                 return;
17185             }
17186             if (obj.parent.constructor != Roo.XComponent) {
17187                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17188             }
17189             if (!obj.parent.modules) {
17190                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17191                     function(o) { return o.order + '' }
17192                 );
17193             }
17194             if (obj.parent.disabled) {
17195                 obj.disabled = true;
17196             }
17197             obj.parent.modules.add(obj);
17198         }, this);
17199     },
17200     
17201      /**
17202      * make a list of modules to build.
17203      * @return {Array} list of modules. 
17204      */ 
17205     
17206     buildOrder : function()
17207     {
17208         var _this = this;
17209         var cmp = function(a,b) {   
17210             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17211         };
17212         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17213             throw "No top level modules to build";
17214         }
17215         
17216         // make a flat list in order of modules to build.
17217         var mods = this.topModule ? [ this.topModule ] : [];
17218                 
17219         
17220         // elmodules (is a list of DOM based modules )
17221         Roo.each(this.elmodules, function(e) {
17222             mods.push(e);
17223             if (!this.topModule &&
17224                 typeof(e.parent) == 'string' &&
17225                 e.parent.substring(0,1) == '#' &&
17226                 Roo.get(e.parent.substr(1))
17227                ) {
17228                 
17229                 _this.topModule = e;
17230             }
17231             
17232         });
17233
17234         
17235         // add modules to their parents..
17236         var addMod = function(m) {
17237             Roo.debug && Roo.log("build Order: add: " + m.name);
17238                 
17239             mods.push(m);
17240             if (m.modules && !m.disabled) {
17241                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17242                 m.modules.keySort('ASC',  cmp );
17243                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17244     
17245                 m.modules.each(addMod);
17246             } else {
17247                 Roo.debug && Roo.log("build Order: no child modules");
17248             }
17249             // not sure if this is used any more..
17250             if (m.finalize) {
17251                 m.finalize.name = m.name + " (clean up) ";
17252                 mods.push(m.finalize);
17253             }
17254             
17255         }
17256         if (this.topModule && this.topModule.modules) { 
17257             this.topModule.modules.keySort('ASC',  cmp );
17258             this.topModule.modules.each(addMod);
17259         } 
17260         return mods;
17261     },
17262     
17263      /**
17264      * Build the registered modules.
17265      * @param {Object} parent element.
17266      * @param {Function} optional method to call after module has been added.
17267      * 
17268      */ 
17269    
17270     build : function(opts) 
17271     {
17272         
17273         if (typeof(opts) != 'undefined') {
17274             Roo.apply(this,opts);
17275         }
17276         
17277         this.preBuild();
17278         var mods = this.buildOrder();
17279       
17280         //this.allmods = mods;
17281         //Roo.debug && Roo.log(mods);
17282         //return;
17283         if (!mods.length) { // should not happen
17284             throw "NO modules!!!";
17285         }
17286         
17287         
17288         var msg = "Building Interface...";
17289         // flash it up as modal - so we store the mask!?
17290         if (!this.hideProgress && Roo.MessageBox) {
17291             Roo.MessageBox.show({ title: 'loading' });
17292             Roo.MessageBox.show({
17293                title: "Please wait...",
17294                msg: msg,
17295                width:450,
17296                progress:true,
17297                buttons : false,
17298                closable:false,
17299                modal: false
17300               
17301             });
17302         }
17303         var total = mods.length;
17304         
17305         var _this = this;
17306         var progressRun = function() {
17307             if (!mods.length) {
17308                 Roo.debug && Roo.log('hide?');
17309                 if (!this.hideProgress && Roo.MessageBox) {
17310                     Roo.MessageBox.hide();
17311                 }
17312                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17313                 
17314                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17315                 
17316                 // THE END...
17317                 return false;   
17318             }
17319             
17320             var m = mods.shift();
17321             
17322             
17323             Roo.debug && Roo.log(m);
17324             // not sure if this is supported any more.. - modules that are are just function
17325             if (typeof(m) == 'function') { 
17326                 m.call(this);
17327                 return progressRun.defer(10, _this);
17328             } 
17329             
17330             
17331             msg = "Building Interface " + (total  - mods.length) + 
17332                     " of " + total + 
17333                     (m.name ? (' - ' + m.name) : '');
17334                         Roo.debug && Roo.log(msg);
17335             if (!_this.hideProgress &&  Roo.MessageBox) { 
17336                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17337             }
17338             
17339          
17340             // is the module disabled?
17341             var disabled = (typeof(m.disabled) == 'function') ?
17342                 m.disabled.call(m.module.disabled) : m.disabled;    
17343             
17344             
17345             if (disabled) {
17346                 return progressRun(); // we do not update the display!
17347             }
17348             
17349             // now build 
17350             
17351                         
17352                         
17353             m.render();
17354             // it's 10 on top level, and 1 on others??? why...
17355             return progressRun.defer(10, _this);
17356              
17357         }
17358         progressRun.defer(1, _this);
17359      
17360         
17361         
17362     },
17363     /**
17364      * Overlay a set of modified strings onto a component
17365      * This is dependant on our builder exporting the strings and 'named strings' elements.
17366      * 
17367      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17368      * @param {Object} associative array of 'named' string and it's new value.
17369      * 
17370      */
17371         overlayStrings : function( component, strings )
17372     {
17373         if (typeof(component['_named_strings']) == 'undefined') {
17374             throw "ERROR: component does not have _named_strings";
17375         }
17376         for ( var k in strings ) {
17377             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17378             if (md !== false) {
17379                 component['_strings'][md] = strings[k];
17380             } else {
17381                 Roo.log('could not find named string: ' + k + ' in');
17382                 Roo.log(component);
17383             }
17384             
17385         }
17386         
17387     },
17388     
17389         
17390         /**
17391          * Event Object.
17392          *
17393          *
17394          */
17395         event: false, 
17396     /**
17397          * wrapper for event.on - aliased later..  
17398          * Typically use to register a event handler for register:
17399          *
17400          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17401          *
17402          */
17403     on : false
17404    
17405     
17406     
17407 });
17408
17409 Roo.XComponent.event = new Roo.util.Observable({
17410                 events : { 
17411                         /**
17412                          * @event register
17413                          * Fires when an Component is registered,
17414                          * set the disable property on the Component to stop registration.
17415                          * @param {Roo.XComponent} c the component being registerd.
17416                          * 
17417                          */
17418                         'register' : true,
17419             /**
17420                          * @event beforebuild
17421                          * Fires before each Component is built
17422                          * can be used to apply permissions.
17423                          * @param {Roo.XComponent} c the component being registerd.
17424                          * 
17425                          */
17426                         'beforebuild' : true,
17427                         /**
17428                          * @event buildcomplete
17429                          * Fires on the top level element when all elements have been built
17430                          * @param {Roo.XComponent} the top level component.
17431                          */
17432                         'buildcomplete' : true
17433                         
17434                 }
17435 });
17436
17437 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17438  //
17439  /**
17440  * marked - a markdown parser
17441  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17442  * https://github.com/chjj/marked
17443  */
17444
17445
17446 /**
17447  *
17448  * Roo.Markdown - is a very crude wrapper around marked..
17449  *
17450  * usage:
17451  * 
17452  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17453  * 
17454  * Note: move the sample code to the bottom of this
17455  * file before uncommenting it.
17456  *
17457  */
17458
17459 Roo.Markdown = {};
17460 Roo.Markdown.toHtml = function(text) {
17461     
17462     var c = new Roo.Markdown.marked.setOptions({
17463             renderer: new Roo.Markdown.marked.Renderer(),
17464             gfm: true,
17465             tables: true,
17466             breaks: false,
17467             pedantic: false,
17468             sanitize: false,
17469             smartLists: true,
17470             smartypants: false
17471           });
17472     // A FEW HACKS!!?
17473     
17474     text = text.replace(/\\\n/g,' ');
17475     return Roo.Markdown.marked(text);
17476 };
17477 //
17478 // converter
17479 //
17480 // Wraps all "globals" so that the only thing
17481 // exposed is makeHtml().
17482 //
17483 (function() {
17484     
17485      /**
17486          * eval:var:escape
17487          * eval:var:unescape
17488          * eval:var:replace
17489          */
17490       
17491     /**
17492      * Helpers
17493      */
17494     
17495     var escape = function (html, encode) {
17496       return html
17497         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17498         .replace(/</g, '&lt;')
17499         .replace(/>/g, '&gt;')
17500         .replace(/"/g, '&quot;')
17501         .replace(/'/g, '&#39;');
17502     }
17503     
17504     var unescape = function (html) {
17505         // explicitly match decimal, hex, and named HTML entities 
17506       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17507         n = n.toLowerCase();
17508         if (n === 'colon') { return ':'; }
17509         if (n.charAt(0) === '#') {
17510           return n.charAt(1) === 'x'
17511             ? String.fromCharCode(parseInt(n.substring(2), 16))
17512             : String.fromCharCode(+n.substring(1));
17513         }
17514         return '';
17515       });
17516     }
17517     
17518     var replace = function (regex, opt) {
17519       regex = regex.source;
17520       opt = opt || '';
17521       return function self(name, val) {
17522         if (!name) { return new RegExp(regex, opt); }
17523         val = val.source || val;
17524         val = val.replace(/(^|[^\[])\^/g, '$1');
17525         regex = regex.replace(name, val);
17526         return self;
17527       };
17528     }
17529
17530
17531          /**
17532          * eval:var:noop
17533     */
17534     var noop = function () {}
17535     noop.exec = noop;
17536     
17537          /**
17538          * eval:var:merge
17539     */
17540     var merge = function (obj) {
17541       var i = 1
17542         , target
17543         , key;
17544     
17545       for (; i < arguments.length; i++) {
17546         target = arguments[i];
17547         for (key in target) {
17548           if (Object.prototype.hasOwnProperty.call(target, key)) {
17549             obj[key] = target[key];
17550           }
17551         }
17552       }
17553     
17554       return obj;
17555     }
17556     
17557     
17558     /**
17559      * Block-Level Grammar
17560      */
17561     
17562     
17563     
17564     
17565     var block = {
17566       newline: /^\n+/,
17567       code: /^( {4}[^\n]+\n*)+/,
17568       fences: noop,
17569       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17570       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17571       nptable: noop,
17572       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17573       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17574       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17575       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17576       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17577       table: noop,
17578       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17579       text: /^[^\n]+/
17580     };
17581     
17582     block.bullet = /(?:[*+-]|\d+\.)/;
17583     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17584     block.item = replace(block.item, 'gm')
17585       (/bull/g, block.bullet)
17586       ();
17587     
17588     block.list = replace(block.list)
17589       (/bull/g, block.bullet)
17590       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17591       ('def', '\\n+(?=' + block.def.source + ')')
17592       ();
17593     
17594     block.blockquote = replace(block.blockquote)
17595       ('def', block.def)
17596       ();
17597     
17598     block._tag = '(?!(?:'
17599       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17600       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17601       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17602     
17603     block.html = replace(block.html)
17604       ('comment', /<!--[\s\S]*?-->/)
17605       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17606       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17607       (/tag/g, block._tag)
17608       ();
17609     
17610     block.paragraph = replace(block.paragraph)
17611       ('hr', block.hr)
17612       ('heading', block.heading)
17613       ('lheading', block.lheading)
17614       ('blockquote', block.blockquote)
17615       ('tag', '<' + block._tag)
17616       ('def', block.def)
17617       ();
17618     
17619     /**
17620      * Normal Block Grammar
17621      */
17622     
17623     block.normal = merge({}, block);
17624     
17625     /**
17626      * GFM Block Grammar
17627      */
17628     
17629     block.gfm = merge({}, block.normal, {
17630       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17631       paragraph: /^/,
17632       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17633     });
17634     
17635     block.gfm.paragraph = replace(block.paragraph)
17636       ('(?!', '(?!'
17637         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17638         + block.list.source.replace('\\1', '\\3') + '|')
17639       ();
17640     
17641     /**
17642      * GFM + Tables Block Grammar
17643      */
17644     
17645     block.tables = merge({}, block.gfm, {
17646       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17647       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17648     });
17649     
17650     /**
17651      * Block Lexer
17652      */
17653     
17654     var Lexer = function (options) {
17655       this.tokens = [];
17656       this.tokens.links = {};
17657       this.options = options || marked.defaults;
17658       this.rules = block.normal;
17659     
17660       if (this.options.gfm) {
17661         if (this.options.tables) {
17662           this.rules = block.tables;
17663         } else {
17664           this.rules = block.gfm;
17665         }
17666       }
17667     }
17668     
17669     /**
17670      * Expose Block Rules
17671      */
17672     
17673     Lexer.rules = block;
17674     
17675     /**
17676      * Static Lex Method
17677      */
17678     
17679     Lexer.lex = function(src, options) {
17680       var lexer = new Lexer(options);
17681       return lexer.lex(src);
17682     };
17683     
17684     /**
17685      * Preprocessing
17686      */
17687     
17688     Lexer.prototype.lex = function(src) {
17689       src = src
17690         .replace(/\r\n|\r/g, '\n')
17691         .replace(/\t/g, '    ')
17692         .replace(/\u00a0/g, ' ')
17693         .replace(/\u2424/g, '\n');
17694     
17695       return this.token(src, true);
17696     };
17697     
17698     /**
17699      * Lexing
17700      */
17701     
17702     Lexer.prototype.token = function(src, top, bq) {
17703       var src = src.replace(/^ +$/gm, '')
17704         , next
17705         , loose
17706         , cap
17707         , bull
17708         , b
17709         , item
17710         , space
17711         , i
17712         , l;
17713     
17714       while (src) {
17715         // newline
17716         if (cap = this.rules.newline.exec(src)) {
17717           src = src.substring(cap[0].length);
17718           if (cap[0].length > 1) {
17719             this.tokens.push({
17720               type: 'space'
17721             });
17722           }
17723         }
17724     
17725         // code
17726         if (cap = this.rules.code.exec(src)) {
17727           src = src.substring(cap[0].length);
17728           cap = cap[0].replace(/^ {4}/gm, '');
17729           this.tokens.push({
17730             type: 'code',
17731             text: !this.options.pedantic
17732               ? cap.replace(/\n+$/, '')
17733               : cap
17734           });
17735           continue;
17736         }
17737     
17738         // fences (gfm)
17739         if (cap = this.rules.fences.exec(src)) {
17740           src = src.substring(cap[0].length);
17741           this.tokens.push({
17742             type: 'code',
17743             lang: cap[2],
17744             text: cap[3] || ''
17745           });
17746           continue;
17747         }
17748     
17749         // heading
17750         if (cap = this.rules.heading.exec(src)) {
17751           src = src.substring(cap[0].length);
17752           this.tokens.push({
17753             type: 'heading',
17754             depth: cap[1].length,
17755             text: cap[2]
17756           });
17757           continue;
17758         }
17759     
17760         // table no leading pipe (gfm)
17761         if (top && (cap = this.rules.nptable.exec(src))) {
17762           src = src.substring(cap[0].length);
17763     
17764           item = {
17765             type: 'table',
17766             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17767             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17768             cells: cap[3].replace(/\n$/, '').split('\n')
17769           };
17770     
17771           for (i = 0; i < item.align.length; i++) {
17772             if (/^ *-+: *$/.test(item.align[i])) {
17773               item.align[i] = 'right';
17774             } else if (/^ *:-+: *$/.test(item.align[i])) {
17775               item.align[i] = 'center';
17776             } else if (/^ *:-+ *$/.test(item.align[i])) {
17777               item.align[i] = 'left';
17778             } else {
17779               item.align[i] = null;
17780             }
17781           }
17782     
17783           for (i = 0; i < item.cells.length; i++) {
17784             item.cells[i] = item.cells[i].split(/ *\| */);
17785           }
17786     
17787           this.tokens.push(item);
17788     
17789           continue;
17790         }
17791     
17792         // lheading
17793         if (cap = this.rules.lheading.exec(src)) {
17794           src = src.substring(cap[0].length);
17795           this.tokens.push({
17796             type: 'heading',
17797             depth: cap[2] === '=' ? 1 : 2,
17798             text: cap[1]
17799           });
17800           continue;
17801         }
17802     
17803         // hr
17804         if (cap = this.rules.hr.exec(src)) {
17805           src = src.substring(cap[0].length);
17806           this.tokens.push({
17807             type: 'hr'
17808           });
17809           continue;
17810         }
17811     
17812         // blockquote
17813         if (cap = this.rules.blockquote.exec(src)) {
17814           src = src.substring(cap[0].length);
17815     
17816           this.tokens.push({
17817             type: 'blockquote_start'
17818           });
17819     
17820           cap = cap[0].replace(/^ *> ?/gm, '');
17821     
17822           // Pass `top` to keep the current
17823           // "toplevel" state. This is exactly
17824           // how markdown.pl works.
17825           this.token(cap, top, true);
17826     
17827           this.tokens.push({
17828             type: 'blockquote_end'
17829           });
17830     
17831           continue;
17832         }
17833     
17834         // list
17835         if (cap = this.rules.list.exec(src)) {
17836           src = src.substring(cap[0].length);
17837           bull = cap[2];
17838     
17839           this.tokens.push({
17840             type: 'list_start',
17841             ordered: bull.length > 1
17842           });
17843     
17844           // Get each top-level item.
17845           cap = cap[0].match(this.rules.item);
17846     
17847           next = false;
17848           l = cap.length;
17849           i = 0;
17850     
17851           for (; i < l; i++) {
17852             item = cap[i];
17853     
17854             // Remove the list item's bullet
17855             // so it is seen as the next token.
17856             space = item.length;
17857             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17858     
17859             // Outdent whatever the
17860             // list item contains. Hacky.
17861             if (~item.indexOf('\n ')) {
17862               space -= item.length;
17863               item = !this.options.pedantic
17864                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17865                 : item.replace(/^ {1,4}/gm, '');
17866             }
17867     
17868             // Determine whether the next list item belongs here.
17869             // Backpedal if it does not belong in this list.
17870             if (this.options.smartLists && i !== l - 1) {
17871               b = block.bullet.exec(cap[i + 1])[0];
17872               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17873                 src = cap.slice(i + 1).join('\n') + src;
17874                 i = l - 1;
17875               }
17876             }
17877     
17878             // Determine whether item is loose or not.
17879             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17880             // for discount behavior.
17881             loose = next || /\n\n(?!\s*$)/.test(item);
17882             if (i !== l - 1) {
17883               next = item.charAt(item.length - 1) === '\n';
17884               if (!loose) { loose = next; }
17885             }
17886     
17887             this.tokens.push({
17888               type: loose
17889                 ? 'loose_item_start'
17890                 : 'list_item_start'
17891             });
17892     
17893             // Recurse.
17894             this.token(item, false, bq);
17895     
17896             this.tokens.push({
17897               type: 'list_item_end'
17898             });
17899           }
17900     
17901           this.tokens.push({
17902             type: 'list_end'
17903           });
17904     
17905           continue;
17906         }
17907     
17908         // html
17909         if (cap = this.rules.html.exec(src)) {
17910           src = src.substring(cap[0].length);
17911           this.tokens.push({
17912             type: this.options.sanitize
17913               ? 'paragraph'
17914               : 'html',
17915             pre: !this.options.sanitizer
17916               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17917             text: cap[0]
17918           });
17919           continue;
17920         }
17921     
17922         // def
17923         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17924           src = src.substring(cap[0].length);
17925           this.tokens.links[cap[1].toLowerCase()] = {
17926             href: cap[2],
17927             title: cap[3]
17928           };
17929           continue;
17930         }
17931     
17932         // table (gfm)
17933         if (top && (cap = this.rules.table.exec(src))) {
17934           src = src.substring(cap[0].length);
17935     
17936           item = {
17937             type: 'table',
17938             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17939             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17940             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17941           };
17942     
17943           for (i = 0; i < item.align.length; i++) {
17944             if (/^ *-+: *$/.test(item.align[i])) {
17945               item.align[i] = 'right';
17946             } else if (/^ *:-+: *$/.test(item.align[i])) {
17947               item.align[i] = 'center';
17948             } else if (/^ *:-+ *$/.test(item.align[i])) {
17949               item.align[i] = 'left';
17950             } else {
17951               item.align[i] = null;
17952             }
17953           }
17954     
17955           for (i = 0; i < item.cells.length; i++) {
17956             item.cells[i] = item.cells[i]
17957               .replace(/^ *\| *| *\| *$/g, '')
17958               .split(/ *\| */);
17959           }
17960     
17961           this.tokens.push(item);
17962     
17963           continue;
17964         }
17965     
17966         // top-level paragraph
17967         if (top && (cap = this.rules.paragraph.exec(src))) {
17968           src = src.substring(cap[0].length);
17969           this.tokens.push({
17970             type: 'paragraph',
17971             text: cap[1].charAt(cap[1].length - 1) === '\n'
17972               ? cap[1].slice(0, -1)
17973               : cap[1]
17974           });
17975           continue;
17976         }
17977     
17978         // text
17979         if (cap = this.rules.text.exec(src)) {
17980           // Top-level should never reach here.
17981           src = src.substring(cap[0].length);
17982           this.tokens.push({
17983             type: 'text',
17984             text: cap[0]
17985           });
17986           continue;
17987         }
17988     
17989         if (src) {
17990           throw new
17991             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17992         }
17993       }
17994     
17995       return this.tokens;
17996     };
17997     
17998     /**
17999      * Inline-Level Grammar
18000      */
18001     
18002     var inline = {
18003       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18004       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18005       url: noop,
18006       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18007       link: /^!?\[(inside)\]\(href\)/,
18008       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18009       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18010       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18011       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18012       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18013       br: /^ {2,}\n(?!\s*$)/,
18014       del: noop,
18015       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18016     };
18017     
18018     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18019     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18020     
18021     inline.link = replace(inline.link)
18022       ('inside', inline._inside)
18023       ('href', inline._href)
18024       ();
18025     
18026     inline.reflink = replace(inline.reflink)
18027       ('inside', inline._inside)
18028       ();
18029     
18030     /**
18031      * Normal Inline Grammar
18032      */
18033     
18034     inline.normal = merge({}, inline);
18035     
18036     /**
18037      * Pedantic Inline Grammar
18038      */
18039     
18040     inline.pedantic = merge({}, inline.normal, {
18041       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18042       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18043     });
18044     
18045     /**
18046      * GFM Inline Grammar
18047      */
18048     
18049     inline.gfm = merge({}, inline.normal, {
18050       escape: replace(inline.escape)('])', '~|])')(),
18051       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18052       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18053       text: replace(inline.text)
18054         (']|', '~]|')
18055         ('|', '|https?://|')
18056         ()
18057     });
18058     
18059     /**
18060      * GFM + Line Breaks Inline Grammar
18061      */
18062     
18063     inline.breaks = merge({}, inline.gfm, {
18064       br: replace(inline.br)('{2,}', '*')(),
18065       text: replace(inline.gfm.text)('{2,}', '*')()
18066     });
18067     
18068     /**
18069      * Inline Lexer & Compiler
18070      */
18071     
18072     var InlineLexer  = function (links, options) {
18073       this.options = options || marked.defaults;
18074       this.links = links;
18075       this.rules = inline.normal;
18076       this.renderer = this.options.renderer || new Renderer;
18077       this.renderer.options = this.options;
18078     
18079       if (!this.links) {
18080         throw new
18081           Error('Tokens array requires a `links` property.');
18082       }
18083     
18084       if (this.options.gfm) {
18085         if (this.options.breaks) {
18086           this.rules = inline.breaks;
18087         } else {
18088           this.rules = inline.gfm;
18089         }
18090       } else if (this.options.pedantic) {
18091         this.rules = inline.pedantic;
18092       }
18093     }
18094     
18095     /**
18096      * Expose Inline Rules
18097      */
18098     
18099     InlineLexer.rules = inline;
18100     
18101     /**
18102      * Static Lexing/Compiling Method
18103      */
18104     
18105     InlineLexer.output = function(src, links, options) {
18106       var inline = new InlineLexer(links, options);
18107       return inline.output(src);
18108     };
18109     
18110     /**
18111      * Lexing/Compiling
18112      */
18113     
18114     InlineLexer.prototype.output = function(src) {
18115       var out = ''
18116         , link
18117         , text
18118         , href
18119         , cap;
18120     
18121       while (src) {
18122         // escape
18123         if (cap = this.rules.escape.exec(src)) {
18124           src = src.substring(cap[0].length);
18125           out += cap[1];
18126           continue;
18127         }
18128     
18129         // autolink
18130         if (cap = this.rules.autolink.exec(src)) {
18131           src = src.substring(cap[0].length);
18132           if (cap[2] === '@') {
18133             text = cap[1].charAt(6) === ':'
18134               ? this.mangle(cap[1].substring(7))
18135               : this.mangle(cap[1]);
18136             href = this.mangle('mailto:') + text;
18137           } else {
18138             text = escape(cap[1]);
18139             href = text;
18140           }
18141           out += this.renderer.link(href, null, text);
18142           continue;
18143         }
18144     
18145         // url (gfm)
18146         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18147           src = src.substring(cap[0].length);
18148           text = escape(cap[1]);
18149           href = text;
18150           out += this.renderer.link(href, null, text);
18151           continue;
18152         }
18153     
18154         // tag
18155         if (cap = this.rules.tag.exec(src)) {
18156           if (!this.inLink && /^<a /i.test(cap[0])) {
18157             this.inLink = true;
18158           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18159             this.inLink = false;
18160           }
18161           src = src.substring(cap[0].length);
18162           out += this.options.sanitize
18163             ? this.options.sanitizer
18164               ? this.options.sanitizer(cap[0])
18165               : escape(cap[0])
18166             : cap[0];
18167           continue;
18168         }
18169     
18170         // link
18171         if (cap = this.rules.link.exec(src)) {
18172           src = src.substring(cap[0].length);
18173           this.inLink = true;
18174           out += this.outputLink(cap, {
18175             href: cap[2],
18176             title: cap[3]
18177           });
18178           this.inLink = false;
18179           continue;
18180         }
18181     
18182         // reflink, nolink
18183         if ((cap = this.rules.reflink.exec(src))
18184             || (cap = this.rules.nolink.exec(src))) {
18185           src = src.substring(cap[0].length);
18186           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18187           link = this.links[link.toLowerCase()];
18188           if (!link || !link.href) {
18189             out += cap[0].charAt(0);
18190             src = cap[0].substring(1) + src;
18191             continue;
18192           }
18193           this.inLink = true;
18194           out += this.outputLink(cap, link);
18195           this.inLink = false;
18196           continue;
18197         }
18198     
18199         // strong
18200         if (cap = this.rules.strong.exec(src)) {
18201           src = src.substring(cap[0].length);
18202           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18203           continue;
18204         }
18205     
18206         // em
18207         if (cap = this.rules.em.exec(src)) {
18208           src = src.substring(cap[0].length);
18209           out += this.renderer.em(this.output(cap[2] || cap[1]));
18210           continue;
18211         }
18212     
18213         // code
18214         if (cap = this.rules.code.exec(src)) {
18215           src = src.substring(cap[0].length);
18216           out += this.renderer.codespan(escape(cap[2], true));
18217           continue;
18218         }
18219     
18220         // br
18221         if (cap = this.rules.br.exec(src)) {
18222           src = src.substring(cap[0].length);
18223           out += this.renderer.br();
18224           continue;
18225         }
18226     
18227         // del (gfm)
18228         if (cap = this.rules.del.exec(src)) {
18229           src = src.substring(cap[0].length);
18230           out += this.renderer.del(this.output(cap[1]));
18231           continue;
18232         }
18233     
18234         // text
18235         if (cap = this.rules.text.exec(src)) {
18236           src = src.substring(cap[0].length);
18237           out += this.renderer.text(escape(this.smartypants(cap[0])));
18238           continue;
18239         }
18240     
18241         if (src) {
18242           throw new
18243             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18244         }
18245       }
18246     
18247       return out;
18248     };
18249     
18250     /**
18251      * Compile Link
18252      */
18253     
18254     InlineLexer.prototype.outputLink = function(cap, link) {
18255       var href = escape(link.href)
18256         , title = link.title ? escape(link.title) : null;
18257     
18258       return cap[0].charAt(0) !== '!'
18259         ? this.renderer.link(href, title, this.output(cap[1]))
18260         : this.renderer.image(href, title, escape(cap[1]));
18261     };
18262     
18263     /**
18264      * Smartypants Transformations
18265      */
18266     
18267     InlineLexer.prototype.smartypants = function(text) {
18268       if (!this.options.smartypants)  { return text; }
18269       return text
18270         // em-dashes
18271         .replace(/---/g, '\u2014')
18272         // en-dashes
18273         .replace(/--/g, '\u2013')
18274         // opening singles
18275         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18276         // closing singles & apostrophes
18277         .replace(/'/g, '\u2019')
18278         // opening doubles
18279         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18280         // closing doubles
18281         .replace(/"/g, '\u201d')
18282         // ellipses
18283         .replace(/\.{3}/g, '\u2026');
18284     };
18285     
18286     /**
18287      * Mangle Links
18288      */
18289     
18290     InlineLexer.prototype.mangle = function(text) {
18291       if (!this.options.mangle) { return text; }
18292       var out = ''
18293         , l = text.length
18294         , i = 0
18295         , ch;
18296     
18297       for (; i < l; i++) {
18298         ch = text.charCodeAt(i);
18299         if (Math.random() > 0.5) {
18300           ch = 'x' + ch.toString(16);
18301         }
18302         out += '&#' + ch + ';';
18303       }
18304     
18305       return out;
18306     };
18307     
18308     /**
18309      * Renderer
18310      */
18311     
18312      /**
18313          * eval:var:Renderer
18314     */
18315     
18316     var Renderer   = function (options) {
18317       this.options = options || {};
18318     }
18319     
18320     Renderer.prototype.code = function(code, lang, escaped) {
18321       if (this.options.highlight) {
18322         var out = this.options.highlight(code, lang);
18323         if (out != null && out !== code) {
18324           escaped = true;
18325           code = out;
18326         }
18327       } else {
18328             // hack!!! - it's already escapeD?
18329             escaped = true;
18330       }
18331     
18332       if (!lang) {
18333         return '<pre><code>'
18334           + (escaped ? code : escape(code, true))
18335           + '\n</code></pre>';
18336       }
18337     
18338       return '<pre><code class="'
18339         + this.options.langPrefix
18340         + escape(lang, true)
18341         + '">'
18342         + (escaped ? code : escape(code, true))
18343         + '\n</code></pre>\n';
18344     };
18345     
18346     Renderer.prototype.blockquote = function(quote) {
18347       return '<blockquote>\n' + quote + '</blockquote>\n';
18348     };
18349     
18350     Renderer.prototype.html = function(html) {
18351       return html;
18352     };
18353     
18354     Renderer.prototype.heading = function(text, level, raw) {
18355       return '<h'
18356         + level
18357         + ' id="'
18358         + this.options.headerPrefix
18359         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18360         + '">'
18361         + text
18362         + '</h'
18363         + level
18364         + '>\n';
18365     };
18366     
18367     Renderer.prototype.hr = function() {
18368       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18369     };
18370     
18371     Renderer.prototype.list = function(body, ordered) {
18372       var type = ordered ? 'ol' : 'ul';
18373       return '<' + type + '>\n' + body + '</' + type + '>\n';
18374     };
18375     
18376     Renderer.prototype.listitem = function(text) {
18377       return '<li>' + text + '</li>\n';
18378     };
18379     
18380     Renderer.prototype.paragraph = function(text) {
18381       return '<p>' + text + '</p>\n';
18382     };
18383     
18384     Renderer.prototype.table = function(header, body) {
18385       return '<table class="table table-striped">\n'
18386         + '<thead>\n'
18387         + header
18388         + '</thead>\n'
18389         + '<tbody>\n'
18390         + body
18391         + '</tbody>\n'
18392         + '</table>\n';
18393     };
18394     
18395     Renderer.prototype.tablerow = function(content) {
18396       return '<tr>\n' + content + '</tr>\n';
18397     };
18398     
18399     Renderer.prototype.tablecell = function(content, flags) {
18400       var type = flags.header ? 'th' : 'td';
18401       var tag = flags.align
18402         ? '<' + type + ' style="text-align:' + flags.align + '">'
18403         : '<' + type + '>';
18404       return tag + content + '</' + type + '>\n';
18405     };
18406     
18407     // span level renderer
18408     Renderer.prototype.strong = function(text) {
18409       return '<strong>' + text + '</strong>';
18410     };
18411     
18412     Renderer.prototype.em = function(text) {
18413       return '<em>' + text + '</em>';
18414     };
18415     
18416     Renderer.prototype.codespan = function(text) {
18417       return '<code>' + text + '</code>';
18418     };
18419     
18420     Renderer.prototype.br = function() {
18421       return this.options.xhtml ? '<br/>' : '<br>';
18422     };
18423     
18424     Renderer.prototype.del = function(text) {
18425       return '<del>' + text + '</del>';
18426     };
18427     
18428     Renderer.prototype.link = function(href, title, text) {
18429       if (this.options.sanitize) {
18430         try {
18431           var prot = decodeURIComponent(unescape(href))
18432             .replace(/[^\w:]/g, '')
18433             .toLowerCase();
18434         } catch (e) {
18435           return '';
18436         }
18437         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18438           return '';
18439         }
18440       }
18441       var out = '<a href="' + href + '"';
18442       if (title) {
18443         out += ' title="' + title + '"';
18444       }
18445       out += '>' + text + '</a>';
18446       return out;
18447     };
18448     
18449     Renderer.prototype.image = function(href, title, text) {
18450       var out = '<img src="' + href + '" alt="' + text + '"';
18451       if (title) {
18452         out += ' title="' + title + '"';
18453       }
18454       out += this.options.xhtml ? '/>' : '>';
18455       return out;
18456     };
18457     
18458     Renderer.prototype.text = function(text) {
18459       return text;
18460     };
18461     
18462     /**
18463      * Parsing & Compiling
18464      */
18465          /**
18466          * eval:var:Parser
18467     */
18468     
18469     var Parser= function (options) {
18470       this.tokens = [];
18471       this.token = null;
18472       this.options = options || marked.defaults;
18473       this.options.renderer = this.options.renderer || new Renderer;
18474       this.renderer = this.options.renderer;
18475       this.renderer.options = this.options;
18476     }
18477     
18478     /**
18479      * Static Parse Method
18480      */
18481     
18482     Parser.parse = function(src, options, renderer) {
18483       var parser = new Parser(options, renderer);
18484       return parser.parse(src);
18485     };
18486     
18487     /**
18488      * Parse Loop
18489      */
18490     
18491     Parser.prototype.parse = function(src) {
18492       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18493       this.tokens = src.reverse();
18494     
18495       var out = '';
18496       while (this.next()) {
18497         out += this.tok();
18498       }
18499     
18500       return out;
18501     };
18502     
18503     /**
18504      * Next Token
18505      */
18506     
18507     Parser.prototype.next = function() {
18508       return this.token = this.tokens.pop();
18509     };
18510     
18511     /**
18512      * Preview Next Token
18513      */
18514     
18515     Parser.prototype.peek = function() {
18516       return this.tokens[this.tokens.length - 1] || 0;
18517     };
18518     
18519     /**
18520      * Parse Text Tokens
18521      */
18522     
18523     Parser.prototype.parseText = function() {
18524       var body = this.token.text;
18525     
18526       while (this.peek().type === 'text') {
18527         body += '\n' + this.next().text;
18528       }
18529     
18530       return this.inline.output(body);
18531     };
18532     
18533     /**
18534      * Parse Current Token
18535      */
18536     
18537     Parser.prototype.tok = function() {
18538       switch (this.token.type) {
18539         case 'space': {
18540           return '';
18541         }
18542         case 'hr': {
18543           return this.renderer.hr();
18544         }
18545         case 'heading': {
18546           return this.renderer.heading(
18547             this.inline.output(this.token.text),
18548             this.token.depth,
18549             this.token.text);
18550         }
18551         case 'code': {
18552           return this.renderer.code(this.token.text,
18553             this.token.lang,
18554             this.token.escaped);
18555         }
18556         case 'table': {
18557           var header = ''
18558             , body = ''
18559             , i
18560             , row
18561             , cell
18562             , flags
18563             , j;
18564     
18565           // header
18566           cell = '';
18567           for (i = 0; i < this.token.header.length; i++) {
18568             flags = { header: true, align: this.token.align[i] };
18569             cell += this.renderer.tablecell(
18570               this.inline.output(this.token.header[i]),
18571               { header: true, align: this.token.align[i] }
18572             );
18573           }
18574           header += this.renderer.tablerow(cell);
18575     
18576           for (i = 0; i < this.token.cells.length; i++) {
18577             row = this.token.cells[i];
18578     
18579             cell = '';
18580             for (j = 0; j < row.length; j++) {
18581               cell += this.renderer.tablecell(
18582                 this.inline.output(row[j]),
18583                 { header: false, align: this.token.align[j] }
18584               );
18585             }
18586     
18587             body += this.renderer.tablerow(cell);
18588           }
18589           return this.renderer.table(header, body);
18590         }
18591         case 'blockquote_start': {
18592           var body = '';
18593     
18594           while (this.next().type !== 'blockquote_end') {
18595             body += this.tok();
18596           }
18597     
18598           return this.renderer.blockquote(body);
18599         }
18600         case 'list_start': {
18601           var body = ''
18602             , ordered = this.token.ordered;
18603     
18604           while (this.next().type !== 'list_end') {
18605             body += this.tok();
18606           }
18607     
18608           return this.renderer.list(body, ordered);
18609         }
18610         case 'list_item_start': {
18611           var body = '';
18612     
18613           while (this.next().type !== 'list_item_end') {
18614             body += this.token.type === 'text'
18615               ? this.parseText()
18616               : this.tok();
18617           }
18618     
18619           return this.renderer.listitem(body);
18620         }
18621         case 'loose_item_start': {
18622           var body = '';
18623     
18624           while (this.next().type !== 'list_item_end') {
18625             body += this.tok();
18626           }
18627     
18628           return this.renderer.listitem(body);
18629         }
18630         case 'html': {
18631           var html = !this.token.pre && !this.options.pedantic
18632             ? this.inline.output(this.token.text)
18633             : this.token.text;
18634           return this.renderer.html(html);
18635         }
18636         case 'paragraph': {
18637           return this.renderer.paragraph(this.inline.output(this.token.text));
18638         }
18639         case 'text': {
18640           return this.renderer.paragraph(this.parseText());
18641         }
18642       }
18643     };
18644   
18645     
18646     /**
18647      * Marked
18648      */
18649          /**
18650          * eval:var:marked
18651     */
18652     var marked = function (src, opt, callback) {
18653       if (callback || typeof opt === 'function') {
18654         if (!callback) {
18655           callback = opt;
18656           opt = null;
18657         }
18658     
18659         opt = merge({}, marked.defaults, opt || {});
18660     
18661         var highlight = opt.highlight
18662           , tokens
18663           , pending
18664           , i = 0;
18665     
18666         try {
18667           tokens = Lexer.lex(src, opt)
18668         } catch (e) {
18669           return callback(e);
18670         }
18671     
18672         pending = tokens.length;
18673          /**
18674          * eval:var:done
18675     */
18676         var done = function(err) {
18677           if (err) {
18678             opt.highlight = highlight;
18679             return callback(err);
18680           }
18681     
18682           var out;
18683     
18684           try {
18685             out = Parser.parse(tokens, opt);
18686           } catch (e) {
18687             err = e;
18688           }
18689     
18690           opt.highlight = highlight;
18691     
18692           return err
18693             ? callback(err)
18694             : callback(null, out);
18695         };
18696     
18697         if (!highlight || highlight.length < 3) {
18698           return done();
18699         }
18700     
18701         delete opt.highlight;
18702     
18703         if (!pending) { return done(); }
18704     
18705         for (; i < tokens.length; i++) {
18706           (function(token) {
18707             if (token.type !== 'code') {
18708               return --pending || done();
18709             }
18710             return highlight(token.text, token.lang, function(err, code) {
18711               if (err) { return done(err); }
18712               if (code == null || code === token.text) {
18713                 return --pending || done();
18714               }
18715               token.text = code;
18716               token.escaped = true;
18717               --pending || done();
18718             });
18719           })(tokens[i]);
18720         }
18721     
18722         return;
18723       }
18724       try {
18725         if (opt) { opt = merge({}, marked.defaults, opt); }
18726         return Parser.parse(Lexer.lex(src, opt), opt);
18727       } catch (e) {
18728         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18729         if ((opt || marked.defaults).silent) {
18730           return '<p>An error occured:</p><pre>'
18731             + escape(e.message + '', true)
18732             + '</pre>';
18733         }
18734         throw e;
18735       }
18736     }
18737     
18738     /**
18739      * Options
18740      */
18741     
18742     marked.options =
18743     marked.setOptions = function(opt) {
18744       merge(marked.defaults, opt);
18745       return marked;
18746     };
18747     
18748     marked.defaults = {
18749       gfm: true,
18750       tables: true,
18751       breaks: false,
18752       pedantic: false,
18753       sanitize: false,
18754       sanitizer: null,
18755       mangle: true,
18756       smartLists: false,
18757       silent: false,
18758       highlight: null,
18759       langPrefix: 'lang-',
18760       smartypants: false,
18761       headerPrefix: '',
18762       renderer: new Renderer,
18763       xhtml: false
18764     };
18765     
18766     /**
18767      * Expose
18768      */
18769     
18770     marked.Parser = Parser;
18771     marked.parser = Parser.parse;
18772     
18773     marked.Renderer = Renderer;
18774     
18775     marked.Lexer = Lexer;
18776     marked.lexer = Lexer.lex;
18777     
18778     marked.InlineLexer = InlineLexer;
18779     marked.inlineLexer = InlineLexer.output;
18780     
18781     marked.parse = marked;
18782     
18783     Roo.Markdown.marked = marked;
18784
18785 })();/*
18786  * Based on:
18787  * Ext JS Library 1.1.1
18788  * Copyright(c) 2006-2007, Ext JS, LLC.
18789  *
18790  * Originally Released Under LGPL - original licence link has changed is not relivant.
18791  *
18792  * Fork - LGPL
18793  * <script type="text/javascript">
18794  */
18795
18796
18797
18798 /*
18799  * These classes are derivatives of the similarly named classes in the YUI Library.
18800  * The original license:
18801  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18802  * Code licensed under the BSD License:
18803  * http://developer.yahoo.net/yui/license.txt
18804  */
18805
18806 (function() {
18807
18808 var Event=Roo.EventManager;
18809 var Dom=Roo.lib.Dom;
18810
18811 /**
18812  * @class Roo.dd.DragDrop
18813  * @extends Roo.util.Observable
18814  * Defines the interface and base operation of items that that can be
18815  * dragged or can be drop targets.  It was designed to be extended, overriding
18816  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18817  * Up to three html elements can be associated with a DragDrop instance:
18818  * <ul>
18819  * <li>linked element: the element that is passed into the constructor.
18820  * This is the element which defines the boundaries for interaction with
18821  * other DragDrop objects.</li>
18822  * <li>handle element(s): The drag operation only occurs if the element that
18823  * was clicked matches a handle element.  By default this is the linked
18824  * element, but there are times that you will want only a portion of the
18825  * linked element to initiate the drag operation, and the setHandleElId()
18826  * method provides a way to define this.</li>
18827  * <li>drag element: this represents the element that would be moved along
18828  * with the cursor during a drag operation.  By default, this is the linked
18829  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18830  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18831  * </li>
18832  * </ul>
18833  * This class should not be instantiated until the onload event to ensure that
18834  * the associated elements are available.
18835  * The following would define a DragDrop obj that would interact with any
18836  * other DragDrop obj in the "group1" group:
18837  * <pre>
18838  *  dd = new Roo.dd.DragDrop("div1", "group1");
18839  * </pre>
18840  * Since none of the event handlers have been implemented, nothing would
18841  * actually happen if you were to run the code above.  Normally you would
18842  * override this class or one of the default implementations, but you can
18843  * also override the methods you want on an instance of the class...
18844  * <pre>
18845  *  dd.onDragDrop = function(e, id) {
18846  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18847  *  }
18848  * </pre>
18849  * @constructor
18850  * @param {String} id of the element that is linked to this instance
18851  * @param {String} sGroup the group of related DragDrop objects
18852  * @param {object} config an object containing configurable attributes
18853  *                Valid properties for DragDrop:
18854  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18855  */
18856 Roo.dd.DragDrop = function(id, sGroup, config) {
18857     if (id) {
18858         this.init(id, sGroup, config);
18859     }
18860     
18861 };
18862
18863 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18864
18865     /**
18866      * The id of the element associated with this object.  This is what we
18867      * refer to as the "linked element" because the size and position of
18868      * this element is used to determine when the drag and drop objects have
18869      * interacted.
18870      * @property id
18871      * @type String
18872      */
18873     id: null,
18874
18875     /**
18876      * Configuration attributes passed into the constructor
18877      * @property config
18878      * @type object
18879      */
18880     config: null,
18881
18882     /**
18883      * The id of the element that will be dragged.  By default this is same
18884      * as the linked element , but could be changed to another element. Ex:
18885      * Roo.dd.DDProxy
18886      * @property dragElId
18887      * @type String
18888      * @private
18889      */
18890     dragElId: null,
18891
18892     /**
18893      * the id of the element that initiates the drag operation.  By default
18894      * this is the linked element, but could be changed to be a child of this
18895      * element.  This lets us do things like only starting the drag when the
18896      * header element within the linked html element is clicked.
18897      * @property handleElId
18898      * @type String
18899      * @private
18900      */
18901     handleElId: null,
18902
18903     /**
18904      * An associative array of HTML tags that will be ignored if clicked.
18905      * @property invalidHandleTypes
18906      * @type {string: string}
18907      */
18908     invalidHandleTypes: null,
18909
18910     /**
18911      * An associative array of ids for elements that will be ignored if clicked
18912      * @property invalidHandleIds
18913      * @type {string: string}
18914      */
18915     invalidHandleIds: null,
18916
18917     /**
18918      * An indexted array of css class names for elements that will be ignored
18919      * if clicked.
18920      * @property invalidHandleClasses
18921      * @type string[]
18922      */
18923     invalidHandleClasses: null,
18924
18925     /**
18926      * The linked element's absolute X position at the time the drag was
18927      * started
18928      * @property startPageX
18929      * @type int
18930      * @private
18931      */
18932     startPageX: 0,
18933
18934     /**
18935      * The linked element's absolute X position at the time the drag was
18936      * started
18937      * @property startPageY
18938      * @type int
18939      * @private
18940      */
18941     startPageY: 0,
18942
18943     /**
18944      * The group defines a logical collection of DragDrop objects that are
18945      * related.  Instances only get events when interacting with other
18946      * DragDrop object in the same group.  This lets us define multiple
18947      * groups using a single DragDrop subclass if we want.
18948      * @property groups
18949      * @type {string: string}
18950      */
18951     groups: null,
18952
18953     /**
18954      * Individual drag/drop instances can be locked.  This will prevent
18955      * onmousedown start drag.
18956      * @property locked
18957      * @type boolean
18958      * @private
18959      */
18960     locked: false,
18961
18962     /**
18963      * Lock this instance
18964      * @method lock
18965      */
18966     lock: function() { this.locked = true; },
18967
18968     /**
18969      * Unlock this instace
18970      * @method unlock
18971      */
18972     unlock: function() { this.locked = false; },
18973
18974     /**
18975      * By default, all insances can be a drop target.  This can be disabled by
18976      * setting isTarget to false.
18977      * @method isTarget
18978      * @type boolean
18979      */
18980     isTarget: true,
18981
18982     /**
18983      * The padding configured for this drag and drop object for calculating
18984      * the drop zone intersection with this object.
18985      * @method padding
18986      * @type int[]
18987      */
18988     padding: null,
18989
18990     /**
18991      * Cached reference to the linked element
18992      * @property _domRef
18993      * @private
18994      */
18995     _domRef: null,
18996
18997     /**
18998      * Internal typeof flag
18999      * @property __ygDragDrop
19000      * @private
19001      */
19002     __ygDragDrop: true,
19003
19004     /**
19005      * Set to true when horizontal contraints are applied
19006      * @property constrainX
19007      * @type boolean
19008      * @private
19009      */
19010     constrainX: false,
19011
19012     /**
19013      * Set to true when vertical contraints are applied
19014      * @property constrainY
19015      * @type boolean
19016      * @private
19017      */
19018     constrainY: false,
19019
19020     /**
19021      * The left constraint
19022      * @property minX
19023      * @type int
19024      * @private
19025      */
19026     minX: 0,
19027
19028     /**
19029      * The right constraint
19030      * @property maxX
19031      * @type int
19032      * @private
19033      */
19034     maxX: 0,
19035
19036     /**
19037      * The up constraint
19038      * @property minY
19039      * @type int
19040      * @type int
19041      * @private
19042      */
19043     minY: 0,
19044
19045     /**
19046      * The down constraint
19047      * @property maxY
19048      * @type int
19049      * @private
19050      */
19051     maxY: 0,
19052
19053     /**
19054      * Maintain offsets when we resetconstraints.  Set to true when you want
19055      * the position of the element relative to its parent to stay the same
19056      * when the page changes
19057      *
19058      * @property maintainOffset
19059      * @type boolean
19060      */
19061     maintainOffset: false,
19062
19063     /**
19064      * Array of pixel locations the element will snap to if we specified a
19065      * horizontal graduation/interval.  This array is generated automatically
19066      * when you define a tick interval.
19067      * @property xTicks
19068      * @type int[]
19069      */
19070     xTicks: null,
19071
19072     /**
19073      * Array of pixel locations the element will snap to if we specified a
19074      * vertical graduation/interval.  This array is generated automatically
19075      * when you define a tick interval.
19076      * @property yTicks
19077      * @type int[]
19078      */
19079     yTicks: null,
19080
19081     /**
19082      * By default the drag and drop instance will only respond to the primary
19083      * button click (left button for a right-handed mouse).  Set to true to
19084      * allow drag and drop to start with any mouse click that is propogated
19085      * by the browser
19086      * @property primaryButtonOnly
19087      * @type boolean
19088      */
19089     primaryButtonOnly: true,
19090
19091     /**
19092      * The availabe property is false until the linked dom element is accessible.
19093      * @property available
19094      * @type boolean
19095      */
19096     available: false,
19097
19098     /**
19099      * By default, drags can only be initiated if the mousedown occurs in the
19100      * region the linked element is.  This is done in part to work around a
19101      * bug in some browsers that mis-report the mousedown if the previous
19102      * mouseup happened outside of the window.  This property is set to true
19103      * if outer handles are defined.
19104      *
19105      * @property hasOuterHandles
19106      * @type boolean
19107      * @default false
19108      */
19109     hasOuterHandles: false,
19110
19111     /**
19112      * Code that executes immediately before the startDrag event
19113      * @method b4StartDrag
19114      * @private
19115      */
19116     b4StartDrag: function(x, y) { },
19117
19118     /**
19119      * Abstract method called after a drag/drop object is clicked
19120      * and the drag or mousedown time thresholds have beeen met.
19121      * @method startDrag
19122      * @param {int} X click location
19123      * @param {int} Y click location
19124      */
19125     startDrag: function(x, y) { /* override this */ },
19126
19127     /**
19128      * Code that executes immediately before the onDrag event
19129      * @method b4Drag
19130      * @private
19131      */
19132     b4Drag: function(e) { },
19133
19134     /**
19135      * Abstract method called during the onMouseMove event while dragging an
19136      * object.
19137      * @method onDrag
19138      * @param {Event} e the mousemove event
19139      */
19140     onDrag: function(e) { /* override this */ },
19141
19142     /**
19143      * Abstract method called when this element fist begins hovering over
19144      * another DragDrop obj
19145      * @method onDragEnter
19146      * @param {Event} e the mousemove event
19147      * @param {String|DragDrop[]} id In POINT mode, the element
19148      * id this is hovering over.  In INTERSECT mode, an array of one or more
19149      * dragdrop items being hovered over.
19150      */
19151     onDragEnter: function(e, id) { /* override this */ },
19152
19153     /**
19154      * Code that executes immediately before the onDragOver event
19155      * @method b4DragOver
19156      * @private
19157      */
19158     b4DragOver: function(e) { },
19159
19160     /**
19161      * Abstract method called when this element is hovering over another
19162      * DragDrop obj
19163      * @method onDragOver
19164      * @param {Event} e the mousemove event
19165      * @param {String|DragDrop[]} id In POINT mode, the element
19166      * id this is hovering over.  In INTERSECT mode, an array of dd items
19167      * being hovered over.
19168      */
19169     onDragOver: function(e, id) { /* override this */ },
19170
19171     /**
19172      * Code that executes immediately before the onDragOut event
19173      * @method b4DragOut
19174      * @private
19175      */
19176     b4DragOut: function(e) { },
19177
19178     /**
19179      * Abstract method called when we are no longer hovering over an element
19180      * @method onDragOut
19181      * @param {Event} e the mousemove event
19182      * @param {String|DragDrop[]} id In POINT mode, the element
19183      * id this was hovering over.  In INTERSECT mode, an array of dd items
19184      * that the mouse is no longer over.
19185      */
19186     onDragOut: function(e, id) { /* override this */ },
19187
19188     /**
19189      * Code that executes immediately before the onDragDrop event
19190      * @method b4DragDrop
19191      * @private
19192      */
19193     b4DragDrop: function(e) { },
19194
19195     /**
19196      * Abstract method called when this item is dropped on another DragDrop
19197      * obj
19198      * @method onDragDrop
19199      * @param {Event} e the mouseup event
19200      * @param {String|DragDrop[]} id In POINT mode, the element
19201      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19202      * was dropped on.
19203      */
19204     onDragDrop: function(e, id) { /* override this */ },
19205
19206     /**
19207      * Abstract method called when this item is dropped on an area with no
19208      * drop target
19209      * @method onInvalidDrop
19210      * @param {Event} e the mouseup event
19211      */
19212     onInvalidDrop: function(e) { /* override this */ },
19213
19214     /**
19215      * Code that executes immediately before the endDrag event
19216      * @method b4EndDrag
19217      * @private
19218      */
19219     b4EndDrag: function(e) { },
19220
19221     /**
19222      * Fired when we are done dragging the object
19223      * @method endDrag
19224      * @param {Event} e the mouseup event
19225      */
19226     endDrag: function(e) { /* override this */ },
19227
19228     /**
19229      * Code executed immediately before the onMouseDown event
19230      * @method b4MouseDown
19231      * @param {Event} e the mousedown event
19232      * @private
19233      */
19234     b4MouseDown: function(e) {  },
19235
19236     /**
19237      * Event handler that fires when a drag/drop obj gets a mousedown
19238      * @method onMouseDown
19239      * @param {Event} e the mousedown event
19240      */
19241     onMouseDown: function(e) { /* override this */ },
19242
19243     /**
19244      * Event handler that fires when a drag/drop obj gets a mouseup
19245      * @method onMouseUp
19246      * @param {Event} e the mouseup event
19247      */
19248     onMouseUp: function(e) { /* override this */ },
19249
19250     /**
19251      * Override the onAvailable method to do what is needed after the initial
19252      * position was determined.
19253      * @method onAvailable
19254      */
19255     onAvailable: function () {
19256     },
19257
19258     /*
19259      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19260      * @type Object
19261      */
19262     defaultPadding : {left:0, right:0, top:0, bottom:0},
19263
19264     /*
19265      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19266  *
19267  * Usage:
19268  <pre><code>
19269  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19270                 { dragElId: "existingProxyDiv" });
19271  dd.startDrag = function(){
19272      this.constrainTo("parent-id");
19273  };
19274  </code></pre>
19275  * Or you can initalize it using the {@link Roo.Element} object:
19276  <pre><code>
19277  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19278      startDrag : function(){
19279          this.constrainTo("parent-id");
19280      }
19281  });
19282  </code></pre>
19283      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19284      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19285      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19286      * an object containing the sides to pad. For example: {right:10, bottom:10}
19287      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19288      */
19289     constrainTo : function(constrainTo, pad, inContent){
19290         if(typeof pad == "number"){
19291             pad = {left: pad, right:pad, top:pad, bottom:pad};
19292         }
19293         pad = pad || this.defaultPadding;
19294         var b = Roo.get(this.getEl()).getBox();
19295         var ce = Roo.get(constrainTo);
19296         var s = ce.getScroll();
19297         var c, cd = ce.dom;
19298         if(cd == document.body){
19299             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19300         }else{
19301             xy = ce.getXY();
19302             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19303         }
19304
19305
19306         var topSpace = b.y - c.y;
19307         var leftSpace = b.x - c.x;
19308
19309         this.resetConstraints();
19310         this.setXConstraint(leftSpace - (pad.left||0), // left
19311                 c.width - leftSpace - b.width - (pad.right||0) //right
19312         );
19313         this.setYConstraint(topSpace - (pad.top||0), //top
19314                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19315         );
19316     },
19317
19318     /**
19319      * Returns a reference to the linked element
19320      * @method getEl
19321      * @return {HTMLElement} the html element
19322      */
19323     getEl: function() {
19324         if (!this._domRef) {
19325             this._domRef = Roo.getDom(this.id);
19326         }
19327
19328         return this._domRef;
19329     },
19330
19331     /**
19332      * Returns a reference to the actual element to drag.  By default this is
19333      * the same as the html element, but it can be assigned to another
19334      * element. An example of this can be found in Roo.dd.DDProxy
19335      * @method getDragEl
19336      * @return {HTMLElement} the html element
19337      */
19338     getDragEl: function() {
19339         return Roo.getDom(this.dragElId);
19340     },
19341
19342     /**
19343      * Sets up the DragDrop object.  Must be called in the constructor of any
19344      * Roo.dd.DragDrop subclass
19345      * @method init
19346      * @param id the id of the linked element
19347      * @param {String} sGroup the group of related items
19348      * @param {object} config configuration attributes
19349      */
19350     init: function(id, sGroup, config) {
19351         this.initTarget(id, sGroup, config);
19352         if (!Roo.isTouch) {
19353             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19354         }
19355         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19356         // Event.on(this.id, "selectstart", Event.preventDefault);
19357     },
19358
19359     /**
19360      * Initializes Targeting functionality only... the object does not
19361      * get a mousedown handler.
19362      * @method initTarget
19363      * @param id the id of the linked element
19364      * @param {String} sGroup the group of related items
19365      * @param {object} config configuration attributes
19366      */
19367     initTarget: function(id, sGroup, config) {
19368
19369         // configuration attributes
19370         this.config = config || {};
19371
19372         // create a local reference to the drag and drop manager
19373         this.DDM = Roo.dd.DDM;
19374         // initialize the groups array
19375         this.groups = {};
19376
19377         // assume that we have an element reference instead of an id if the
19378         // parameter is not a string
19379         if (typeof id !== "string") {
19380             id = Roo.id(id);
19381         }
19382
19383         // set the id
19384         this.id = id;
19385
19386         // add to an interaction group
19387         this.addToGroup((sGroup) ? sGroup : "default");
19388
19389         // We don't want to register this as the handle with the manager
19390         // so we just set the id rather than calling the setter.
19391         this.handleElId = id;
19392
19393         // the linked element is the element that gets dragged by default
19394         this.setDragElId(id);
19395
19396         // by default, clicked anchors will not start drag operations.
19397         this.invalidHandleTypes = { A: "A" };
19398         this.invalidHandleIds = {};
19399         this.invalidHandleClasses = [];
19400
19401         this.applyConfig();
19402
19403         this.handleOnAvailable();
19404     },
19405
19406     /**
19407      * Applies the configuration parameters that were passed into the constructor.
19408      * This is supposed to happen at each level through the inheritance chain.  So
19409      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19410      * DragDrop in order to get all of the parameters that are available in
19411      * each object.
19412      * @method applyConfig
19413      */
19414     applyConfig: function() {
19415
19416         // configurable properties:
19417         //    padding, isTarget, maintainOffset, primaryButtonOnly
19418         this.padding           = this.config.padding || [0, 0, 0, 0];
19419         this.isTarget          = (this.config.isTarget !== false);
19420         this.maintainOffset    = (this.config.maintainOffset);
19421         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19422
19423     },
19424
19425     /**
19426      * Executed when the linked element is available
19427      * @method handleOnAvailable
19428      * @private
19429      */
19430     handleOnAvailable: function() {
19431         this.available = true;
19432         this.resetConstraints();
19433         this.onAvailable();
19434     },
19435
19436      /**
19437      * Configures the padding for the target zone in px.  Effectively expands
19438      * (or reduces) the virtual object size for targeting calculations.
19439      * Supports css-style shorthand; if only one parameter is passed, all sides
19440      * will have that padding, and if only two are passed, the top and bottom
19441      * will have the first param, the left and right the second.
19442      * @method setPadding
19443      * @param {int} iTop    Top pad
19444      * @param {int} iRight  Right pad
19445      * @param {int} iBot    Bot pad
19446      * @param {int} iLeft   Left pad
19447      */
19448     setPadding: function(iTop, iRight, iBot, iLeft) {
19449         // this.padding = [iLeft, iRight, iTop, iBot];
19450         if (!iRight && 0 !== iRight) {
19451             this.padding = [iTop, iTop, iTop, iTop];
19452         } else if (!iBot && 0 !== iBot) {
19453             this.padding = [iTop, iRight, iTop, iRight];
19454         } else {
19455             this.padding = [iTop, iRight, iBot, iLeft];
19456         }
19457     },
19458
19459     /**
19460      * Stores the initial placement of the linked element.
19461      * @method setInitialPosition
19462      * @param {int} diffX   the X offset, default 0
19463      * @param {int} diffY   the Y offset, default 0
19464      */
19465     setInitPosition: function(diffX, diffY) {
19466         var el = this.getEl();
19467
19468         if (!this.DDM.verifyEl(el)) {
19469             return;
19470         }
19471
19472         var dx = diffX || 0;
19473         var dy = diffY || 0;
19474
19475         var p = Dom.getXY( el );
19476
19477         this.initPageX = p[0] - dx;
19478         this.initPageY = p[1] - dy;
19479
19480         this.lastPageX = p[0];
19481         this.lastPageY = p[1];
19482
19483
19484         this.setStartPosition(p);
19485     },
19486
19487     /**
19488      * Sets the start position of the element.  This is set when the obj
19489      * is initialized, the reset when a drag is started.
19490      * @method setStartPosition
19491      * @param pos current position (from previous lookup)
19492      * @private
19493      */
19494     setStartPosition: function(pos) {
19495         var p = pos || Dom.getXY( this.getEl() );
19496         this.deltaSetXY = null;
19497
19498         this.startPageX = p[0];
19499         this.startPageY = p[1];
19500     },
19501
19502     /**
19503      * Add this instance to a group of related drag/drop objects.  All
19504      * instances belong to at least one group, and can belong to as many
19505      * groups as needed.
19506      * @method addToGroup
19507      * @param sGroup {string} the name of the group
19508      */
19509     addToGroup: function(sGroup) {
19510         this.groups[sGroup] = true;
19511         this.DDM.regDragDrop(this, sGroup);
19512     },
19513
19514     /**
19515      * Remove's this instance from the supplied interaction group
19516      * @method removeFromGroup
19517      * @param {string}  sGroup  The group to drop
19518      */
19519     removeFromGroup: function(sGroup) {
19520         if (this.groups[sGroup]) {
19521             delete this.groups[sGroup];
19522         }
19523
19524         this.DDM.removeDDFromGroup(this, sGroup);
19525     },
19526
19527     /**
19528      * Allows you to specify that an element other than the linked element
19529      * will be moved with the cursor during a drag
19530      * @method setDragElId
19531      * @param id {string} the id of the element that will be used to initiate the drag
19532      */
19533     setDragElId: function(id) {
19534         this.dragElId = id;
19535     },
19536
19537     /**
19538      * Allows you to specify a child of the linked element that should be
19539      * used to initiate the drag operation.  An example of this would be if
19540      * you have a content div with text and links.  Clicking anywhere in the
19541      * content area would normally start the drag operation.  Use this method
19542      * to specify that an element inside of the content div is the element
19543      * that starts the drag operation.
19544      * @method setHandleElId
19545      * @param id {string} the id of the element that will be used to
19546      * initiate the drag.
19547      */
19548     setHandleElId: function(id) {
19549         if (typeof id !== "string") {
19550             id = Roo.id(id);
19551         }
19552         this.handleElId = id;
19553         this.DDM.regHandle(this.id, id);
19554     },
19555
19556     /**
19557      * Allows you to set an element outside of the linked element as a drag
19558      * handle
19559      * @method setOuterHandleElId
19560      * @param id the id of the element that will be used to initiate the drag
19561      */
19562     setOuterHandleElId: function(id) {
19563         if (typeof id !== "string") {
19564             id = Roo.id(id);
19565         }
19566         Event.on(id, "mousedown",
19567                 this.handleMouseDown, this);
19568         this.setHandleElId(id);
19569
19570         this.hasOuterHandles = true;
19571     },
19572
19573     /**
19574      * Remove all drag and drop hooks for this element
19575      * @method unreg
19576      */
19577     unreg: function() {
19578         Event.un(this.id, "mousedown",
19579                 this.handleMouseDown);
19580         Event.un(this.id, "touchstart",
19581                 this.handleMouseDown);
19582         this._domRef = null;
19583         this.DDM._remove(this);
19584     },
19585
19586     destroy : function(){
19587         this.unreg();
19588     },
19589
19590     /**
19591      * Returns true if this instance is locked, or the drag drop mgr is locked
19592      * (meaning that all drag/drop is disabled on the page.)
19593      * @method isLocked
19594      * @return {boolean} true if this obj or all drag/drop is locked, else
19595      * false
19596      */
19597     isLocked: function() {
19598         return (this.DDM.isLocked() || this.locked);
19599     },
19600
19601     /**
19602      * Fired when this object is clicked
19603      * @method handleMouseDown
19604      * @param {Event} e
19605      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19606      * @private
19607      */
19608     handleMouseDown: function(e, oDD){
19609      
19610         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19611             //Roo.log('not touch/ button !=0');
19612             return;
19613         }
19614         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19615             return; // double touch..
19616         }
19617         
19618
19619         if (this.isLocked()) {
19620             //Roo.log('locked');
19621             return;
19622         }
19623
19624         this.DDM.refreshCache(this.groups);
19625 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19626         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19627         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19628             //Roo.log('no outer handes or not over target');
19629                 // do nothing.
19630         } else {
19631 //            Roo.log('check validator');
19632             if (this.clickValidator(e)) {
19633 //                Roo.log('validate success');
19634                 // set the initial element position
19635                 this.setStartPosition();
19636
19637
19638                 this.b4MouseDown(e);
19639                 this.onMouseDown(e);
19640
19641                 this.DDM.handleMouseDown(e, this);
19642
19643                 this.DDM.stopEvent(e);
19644             } else {
19645
19646
19647             }
19648         }
19649     },
19650
19651     clickValidator: function(e) {
19652         var target = e.getTarget();
19653         return ( this.isValidHandleChild(target) &&
19654                     (this.id == this.handleElId ||
19655                         this.DDM.handleWasClicked(target, this.id)) );
19656     },
19657
19658     /**
19659      * Allows you to specify a tag name that should not start a drag operation
19660      * when clicked.  This is designed to facilitate embedding links within a
19661      * drag handle that do something other than start the drag.
19662      * @method addInvalidHandleType
19663      * @param {string} tagName the type of element to exclude
19664      */
19665     addInvalidHandleType: function(tagName) {
19666         var type = tagName.toUpperCase();
19667         this.invalidHandleTypes[type] = type;
19668     },
19669
19670     /**
19671      * Lets you to specify an element id for a child of a drag handle
19672      * that should not initiate a drag
19673      * @method addInvalidHandleId
19674      * @param {string} id the element id of the element you wish to ignore
19675      */
19676     addInvalidHandleId: function(id) {
19677         if (typeof id !== "string") {
19678             id = Roo.id(id);
19679         }
19680         this.invalidHandleIds[id] = id;
19681     },
19682
19683     /**
19684      * Lets you specify a css class of elements that will not initiate a drag
19685      * @method addInvalidHandleClass
19686      * @param {string} cssClass the class of the elements you wish to ignore
19687      */
19688     addInvalidHandleClass: function(cssClass) {
19689         this.invalidHandleClasses.push(cssClass);
19690     },
19691
19692     /**
19693      * Unsets an excluded tag name set by addInvalidHandleType
19694      * @method removeInvalidHandleType
19695      * @param {string} tagName the type of element to unexclude
19696      */
19697     removeInvalidHandleType: function(tagName) {
19698         var type = tagName.toUpperCase();
19699         // this.invalidHandleTypes[type] = null;
19700         delete this.invalidHandleTypes[type];
19701     },
19702
19703     /**
19704      * Unsets an invalid handle id
19705      * @method removeInvalidHandleId
19706      * @param {string} id the id of the element to re-enable
19707      */
19708     removeInvalidHandleId: function(id) {
19709         if (typeof id !== "string") {
19710             id = Roo.id(id);
19711         }
19712         delete this.invalidHandleIds[id];
19713     },
19714
19715     /**
19716      * Unsets an invalid css class
19717      * @method removeInvalidHandleClass
19718      * @param {string} cssClass the class of the element(s) you wish to
19719      * re-enable
19720      */
19721     removeInvalidHandleClass: function(cssClass) {
19722         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19723             if (this.invalidHandleClasses[i] == cssClass) {
19724                 delete this.invalidHandleClasses[i];
19725             }
19726         }
19727     },
19728
19729     /**
19730      * Checks the tag exclusion list to see if this click should be ignored
19731      * @method isValidHandleChild
19732      * @param {HTMLElement} node the HTMLElement to evaluate
19733      * @return {boolean} true if this is a valid tag type, false if not
19734      */
19735     isValidHandleChild: function(node) {
19736
19737         var valid = true;
19738         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19739         var nodeName;
19740         try {
19741             nodeName = node.nodeName.toUpperCase();
19742         } catch(e) {
19743             nodeName = node.nodeName;
19744         }
19745         valid = valid && !this.invalidHandleTypes[nodeName];
19746         valid = valid && !this.invalidHandleIds[node.id];
19747
19748         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19749             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19750         }
19751
19752
19753         return valid;
19754
19755     },
19756
19757     /**
19758      * Create the array of horizontal tick marks if an interval was specified
19759      * in setXConstraint().
19760      * @method setXTicks
19761      * @private
19762      */
19763     setXTicks: function(iStartX, iTickSize) {
19764         this.xTicks = [];
19765         this.xTickSize = iTickSize;
19766
19767         var tickMap = {};
19768
19769         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19770             if (!tickMap[i]) {
19771                 this.xTicks[this.xTicks.length] = i;
19772                 tickMap[i] = true;
19773             }
19774         }
19775
19776         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19777             if (!tickMap[i]) {
19778                 this.xTicks[this.xTicks.length] = i;
19779                 tickMap[i] = true;
19780             }
19781         }
19782
19783         this.xTicks.sort(this.DDM.numericSort) ;
19784     },
19785
19786     /**
19787      * Create the array of vertical tick marks if an interval was specified in
19788      * setYConstraint().
19789      * @method setYTicks
19790      * @private
19791      */
19792     setYTicks: function(iStartY, iTickSize) {
19793         this.yTicks = [];
19794         this.yTickSize = iTickSize;
19795
19796         var tickMap = {};
19797
19798         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19799             if (!tickMap[i]) {
19800                 this.yTicks[this.yTicks.length] = i;
19801                 tickMap[i] = true;
19802             }
19803         }
19804
19805         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19806             if (!tickMap[i]) {
19807                 this.yTicks[this.yTicks.length] = i;
19808                 tickMap[i] = true;
19809             }
19810         }
19811
19812         this.yTicks.sort(this.DDM.numericSort) ;
19813     },
19814
19815     /**
19816      * By default, the element can be dragged any place on the screen.  Use
19817      * this method to limit the horizontal travel of the element.  Pass in
19818      * 0,0 for the parameters if you want to lock the drag to the y axis.
19819      * @method setXConstraint
19820      * @param {int} iLeft the number of pixels the element can move to the left
19821      * @param {int} iRight the number of pixels the element can move to the
19822      * right
19823      * @param {int} iTickSize optional parameter for specifying that the
19824      * element
19825      * should move iTickSize pixels at a time.
19826      */
19827     setXConstraint: function(iLeft, iRight, iTickSize) {
19828         this.leftConstraint = iLeft;
19829         this.rightConstraint = iRight;
19830
19831         this.minX = this.initPageX - iLeft;
19832         this.maxX = this.initPageX + iRight;
19833         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19834
19835         this.constrainX = true;
19836     },
19837
19838     /**
19839      * Clears any constraints applied to this instance.  Also clears ticks
19840      * since they can't exist independent of a constraint at this time.
19841      * @method clearConstraints
19842      */
19843     clearConstraints: function() {
19844         this.constrainX = false;
19845         this.constrainY = false;
19846         this.clearTicks();
19847     },
19848
19849     /**
19850      * Clears any tick interval defined for this instance
19851      * @method clearTicks
19852      */
19853     clearTicks: function() {
19854         this.xTicks = null;
19855         this.yTicks = null;
19856         this.xTickSize = 0;
19857         this.yTickSize = 0;
19858     },
19859
19860     /**
19861      * By default, the element can be dragged any place on the screen.  Set
19862      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19863      * parameters if you want to lock the drag to the x axis.
19864      * @method setYConstraint
19865      * @param {int} iUp the number of pixels the element can move up
19866      * @param {int} iDown the number of pixels the element can move down
19867      * @param {int} iTickSize optional parameter for specifying that the
19868      * element should move iTickSize pixels at a time.
19869      */
19870     setYConstraint: function(iUp, iDown, iTickSize) {
19871         this.topConstraint = iUp;
19872         this.bottomConstraint = iDown;
19873
19874         this.minY = this.initPageY - iUp;
19875         this.maxY = this.initPageY + iDown;
19876         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19877
19878         this.constrainY = true;
19879
19880     },
19881
19882     /**
19883      * resetConstraints must be called if you manually reposition a dd element.
19884      * @method resetConstraints
19885      * @param {boolean} maintainOffset
19886      */
19887     resetConstraints: function() {
19888
19889
19890         // Maintain offsets if necessary
19891         if (this.initPageX || this.initPageX === 0) {
19892             // figure out how much this thing has moved
19893             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19894             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19895
19896             this.setInitPosition(dx, dy);
19897
19898         // This is the first time we have detected the element's position
19899         } else {
19900             this.setInitPosition();
19901         }
19902
19903         if (this.constrainX) {
19904             this.setXConstraint( this.leftConstraint,
19905                                  this.rightConstraint,
19906                                  this.xTickSize        );
19907         }
19908
19909         if (this.constrainY) {
19910             this.setYConstraint( this.topConstraint,
19911                                  this.bottomConstraint,
19912                                  this.yTickSize         );
19913         }
19914     },
19915
19916     /**
19917      * Normally the drag element is moved pixel by pixel, but we can specify
19918      * that it move a number of pixels at a time.  This method resolves the
19919      * location when we have it set up like this.
19920      * @method getTick
19921      * @param {int} val where we want to place the object
19922      * @param {int[]} tickArray sorted array of valid points
19923      * @return {int} the closest tick
19924      * @private
19925      */
19926     getTick: function(val, tickArray) {
19927
19928         if (!tickArray) {
19929             // If tick interval is not defined, it is effectively 1 pixel,
19930             // so we return the value passed to us.
19931             return val;
19932         } else if (tickArray[0] >= val) {
19933             // The value is lower than the first tick, so we return the first
19934             // tick.
19935             return tickArray[0];
19936         } else {
19937             for (var i=0, len=tickArray.length; i<len; ++i) {
19938                 var next = i + 1;
19939                 if (tickArray[next] && tickArray[next] >= val) {
19940                     var diff1 = val - tickArray[i];
19941                     var diff2 = tickArray[next] - val;
19942                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19943                 }
19944             }
19945
19946             // The value is larger than the last tick, so we return the last
19947             // tick.
19948             return tickArray[tickArray.length - 1];
19949         }
19950     },
19951
19952     /**
19953      * toString method
19954      * @method toString
19955      * @return {string} string representation of the dd obj
19956      */
19957     toString: function() {
19958         return ("DragDrop " + this.id);
19959     }
19960
19961 });
19962
19963 })();
19964 /*
19965  * Based on:
19966  * Ext JS Library 1.1.1
19967  * Copyright(c) 2006-2007, Ext JS, LLC.
19968  *
19969  * Originally Released Under LGPL - original licence link has changed is not relivant.
19970  *
19971  * Fork - LGPL
19972  * <script type="text/javascript">
19973  */
19974
19975
19976 /**
19977  * The drag and drop utility provides a framework for building drag and drop
19978  * applications.  In addition to enabling drag and drop for specific elements,
19979  * the drag and drop elements are tracked by the manager class, and the
19980  * interactions between the various elements are tracked during the drag and
19981  * the implementing code is notified about these important moments.
19982  */
19983
19984 // Only load the library once.  Rewriting the manager class would orphan
19985 // existing drag and drop instances.
19986 if (!Roo.dd.DragDropMgr) {
19987
19988 /**
19989  * @class Roo.dd.DragDropMgr
19990  * DragDropMgr is a singleton that tracks the element interaction for
19991  * all DragDrop items in the window.  Generally, you will not call
19992  * this class directly, but it does have helper methods that could
19993  * be useful in your DragDrop implementations.
19994  * @singleton
19995  */
19996 Roo.dd.DragDropMgr = function() {
19997
19998     var Event = Roo.EventManager;
19999
20000     return {
20001
20002         /**
20003          * Two dimensional Array of registered DragDrop objects.  The first
20004          * dimension is the DragDrop item group, the second the DragDrop
20005          * object.
20006          * @property ids
20007          * @type {string: string}
20008          * @private
20009          * @static
20010          */
20011         ids: {},
20012
20013         /**
20014          * Array of element ids defined as drag handles.  Used to determine
20015          * if the element that generated the mousedown event is actually the
20016          * handle and not the html element itself.
20017          * @property handleIds
20018          * @type {string: string}
20019          * @private
20020          * @static
20021          */
20022         handleIds: {},
20023
20024         /**
20025          * the DragDrop object that is currently being dragged
20026          * @property dragCurrent
20027          * @type DragDrop
20028          * @private
20029          * @static
20030          **/
20031         dragCurrent: null,
20032
20033         /**
20034          * the DragDrop object(s) that are being hovered over
20035          * @property dragOvers
20036          * @type Array
20037          * @private
20038          * @static
20039          */
20040         dragOvers: {},
20041
20042         /**
20043          * the X distance between the cursor and the object being dragged
20044          * @property deltaX
20045          * @type int
20046          * @private
20047          * @static
20048          */
20049         deltaX: 0,
20050
20051         /**
20052          * the Y distance between the cursor and the object being dragged
20053          * @property deltaY
20054          * @type int
20055          * @private
20056          * @static
20057          */
20058         deltaY: 0,
20059
20060         /**
20061          * Flag to determine if we should prevent the default behavior of the
20062          * events we define. By default this is true, but this can be set to
20063          * false if you need the default behavior (not recommended)
20064          * @property preventDefault
20065          * @type boolean
20066          * @static
20067          */
20068         preventDefault: true,
20069
20070         /**
20071          * Flag to determine if we should stop the propagation of the events
20072          * we generate. This is true by default but you may want to set it to
20073          * false if the html element contains other features that require the
20074          * mouse click.
20075          * @property stopPropagation
20076          * @type boolean
20077          * @static
20078          */
20079         stopPropagation: true,
20080
20081         /**
20082          * Internal flag that is set to true when drag and drop has been
20083          * intialized
20084          * @property initialized
20085          * @private
20086          * @static
20087          */
20088         initalized: false,
20089
20090         /**
20091          * All drag and drop can be disabled.
20092          * @property locked
20093          * @private
20094          * @static
20095          */
20096         locked: false,
20097
20098         /**
20099          * Called the first time an element is registered.
20100          * @method init
20101          * @private
20102          * @static
20103          */
20104         init: function() {
20105             this.initialized = true;
20106         },
20107
20108         /**
20109          * In point mode, drag and drop interaction is defined by the
20110          * location of the cursor during the drag/drop
20111          * @property POINT
20112          * @type int
20113          * @static
20114          */
20115         POINT: 0,
20116
20117         /**
20118          * In intersect mode, drag and drop interactio nis defined by the
20119          * overlap of two or more drag and drop objects.
20120          * @property INTERSECT
20121          * @type int
20122          * @static
20123          */
20124         INTERSECT: 1,
20125
20126         /**
20127          * The current drag and drop mode.  Default: POINT
20128          * @property mode
20129          * @type int
20130          * @static
20131          */
20132         mode: 0,
20133
20134         /**
20135          * Runs method on all drag and drop objects
20136          * @method _execOnAll
20137          * @private
20138          * @static
20139          */
20140         _execOnAll: function(sMethod, args) {
20141             for (var i in this.ids) {
20142                 for (var j in this.ids[i]) {
20143                     var oDD = this.ids[i][j];
20144                     if (! this.isTypeOfDD(oDD)) {
20145                         continue;
20146                     }
20147                     oDD[sMethod].apply(oDD, args);
20148                 }
20149             }
20150         },
20151
20152         /**
20153          * Drag and drop initialization.  Sets up the global event handlers
20154          * @method _onLoad
20155          * @private
20156          * @static
20157          */
20158         _onLoad: function() {
20159
20160             this.init();
20161
20162             if (!Roo.isTouch) {
20163                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20164                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20165             }
20166             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20167             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20168             
20169             Event.on(window,   "unload",    this._onUnload, this, true);
20170             Event.on(window,   "resize",    this._onResize, this, true);
20171             // Event.on(window,   "mouseout",    this._test);
20172
20173         },
20174
20175         /**
20176          * Reset constraints on all drag and drop objs
20177          * @method _onResize
20178          * @private
20179          * @static
20180          */
20181         _onResize: function(e) {
20182             this._execOnAll("resetConstraints", []);
20183         },
20184
20185         /**
20186          * Lock all drag and drop functionality
20187          * @method lock
20188          * @static
20189          */
20190         lock: function() { this.locked = true; },
20191
20192         /**
20193          * Unlock all drag and drop functionality
20194          * @method unlock
20195          * @static
20196          */
20197         unlock: function() { this.locked = false; },
20198
20199         /**
20200          * Is drag and drop locked?
20201          * @method isLocked
20202          * @return {boolean} True if drag and drop is locked, false otherwise.
20203          * @static
20204          */
20205         isLocked: function() { return this.locked; },
20206
20207         /**
20208          * Location cache that is set for all drag drop objects when a drag is
20209          * initiated, cleared when the drag is finished.
20210          * @property locationCache
20211          * @private
20212          * @static
20213          */
20214         locationCache: {},
20215
20216         /**
20217          * Set useCache to false if you want to force object the lookup of each
20218          * drag and drop linked element constantly during a drag.
20219          * @property useCache
20220          * @type boolean
20221          * @static
20222          */
20223         useCache: true,
20224
20225         /**
20226          * The number of pixels that the mouse needs to move after the
20227          * mousedown before the drag is initiated.  Default=3;
20228          * @property clickPixelThresh
20229          * @type int
20230          * @static
20231          */
20232         clickPixelThresh: 3,
20233
20234         /**
20235          * The number of milliseconds after the mousedown event to initiate the
20236          * drag if we don't get a mouseup event. Default=1000
20237          * @property clickTimeThresh
20238          * @type int
20239          * @static
20240          */
20241         clickTimeThresh: 350,
20242
20243         /**
20244          * Flag that indicates that either the drag pixel threshold or the
20245          * mousdown time threshold has been met
20246          * @property dragThreshMet
20247          * @type boolean
20248          * @private
20249          * @static
20250          */
20251         dragThreshMet: false,
20252
20253         /**
20254          * Timeout used for the click time threshold
20255          * @property clickTimeout
20256          * @type Object
20257          * @private
20258          * @static
20259          */
20260         clickTimeout: null,
20261
20262         /**
20263          * The X position of the mousedown event stored for later use when a
20264          * drag threshold is met.
20265          * @property startX
20266          * @type int
20267          * @private
20268          * @static
20269          */
20270         startX: 0,
20271
20272         /**
20273          * The Y position of the mousedown event stored for later use when a
20274          * drag threshold is met.
20275          * @property startY
20276          * @type int
20277          * @private
20278          * @static
20279          */
20280         startY: 0,
20281
20282         /**
20283          * Each DragDrop instance must be registered with the DragDropMgr.
20284          * This is executed in DragDrop.init()
20285          * @method regDragDrop
20286          * @param {DragDrop} oDD the DragDrop object to register
20287          * @param {String} sGroup the name of the group this element belongs to
20288          * @static
20289          */
20290         regDragDrop: function(oDD, sGroup) {
20291             if (!this.initialized) { this.init(); }
20292
20293             if (!this.ids[sGroup]) {
20294                 this.ids[sGroup] = {};
20295             }
20296             this.ids[sGroup][oDD.id] = oDD;
20297         },
20298
20299         /**
20300          * Removes the supplied dd instance from the supplied group. Executed
20301          * by DragDrop.removeFromGroup, so don't call this function directly.
20302          * @method removeDDFromGroup
20303          * @private
20304          * @static
20305          */
20306         removeDDFromGroup: function(oDD, sGroup) {
20307             if (!this.ids[sGroup]) {
20308                 this.ids[sGroup] = {};
20309             }
20310
20311             var obj = this.ids[sGroup];
20312             if (obj && obj[oDD.id]) {
20313                 delete obj[oDD.id];
20314             }
20315         },
20316
20317         /**
20318          * Unregisters a drag and drop item.  This is executed in
20319          * DragDrop.unreg, use that method instead of calling this directly.
20320          * @method _remove
20321          * @private
20322          * @static
20323          */
20324         _remove: function(oDD) {
20325             for (var g in oDD.groups) {
20326                 if (g && this.ids[g][oDD.id]) {
20327                     delete this.ids[g][oDD.id];
20328                 }
20329             }
20330             delete this.handleIds[oDD.id];
20331         },
20332
20333         /**
20334          * Each DragDrop handle element must be registered.  This is done
20335          * automatically when executing DragDrop.setHandleElId()
20336          * @method regHandle
20337          * @param {String} sDDId the DragDrop id this element is a handle for
20338          * @param {String} sHandleId the id of the element that is the drag
20339          * handle
20340          * @static
20341          */
20342         regHandle: function(sDDId, sHandleId) {
20343             if (!this.handleIds[sDDId]) {
20344                 this.handleIds[sDDId] = {};
20345             }
20346             this.handleIds[sDDId][sHandleId] = sHandleId;
20347         },
20348
20349         /**
20350          * Utility function to determine if a given element has been
20351          * registered as a drag drop item.
20352          * @method isDragDrop
20353          * @param {String} id the element id to check
20354          * @return {boolean} true if this element is a DragDrop item,
20355          * false otherwise
20356          * @static
20357          */
20358         isDragDrop: function(id) {
20359             return ( this.getDDById(id) ) ? true : false;
20360         },
20361
20362         /**
20363          * Returns the drag and drop instances that are in all groups the
20364          * passed in instance belongs to.
20365          * @method getRelated
20366          * @param {DragDrop} p_oDD the obj to get related data for
20367          * @param {boolean} bTargetsOnly if true, only return targetable objs
20368          * @return {DragDrop[]} the related instances
20369          * @static
20370          */
20371         getRelated: function(p_oDD, bTargetsOnly) {
20372             var oDDs = [];
20373             for (var i in p_oDD.groups) {
20374                 for (j in this.ids[i]) {
20375                     var dd = this.ids[i][j];
20376                     if (! this.isTypeOfDD(dd)) {
20377                         continue;
20378                     }
20379                     if (!bTargetsOnly || dd.isTarget) {
20380                         oDDs[oDDs.length] = dd;
20381                     }
20382                 }
20383             }
20384
20385             return oDDs;
20386         },
20387
20388         /**
20389          * Returns true if the specified dd target is a legal target for
20390          * the specifice drag obj
20391          * @method isLegalTarget
20392          * @param {DragDrop} the drag obj
20393          * @param {DragDrop} the target
20394          * @return {boolean} true if the target is a legal target for the
20395          * dd obj
20396          * @static
20397          */
20398         isLegalTarget: function (oDD, oTargetDD) {
20399             var targets = this.getRelated(oDD, true);
20400             for (var i=0, len=targets.length;i<len;++i) {
20401                 if (targets[i].id == oTargetDD.id) {
20402                     return true;
20403                 }
20404             }
20405
20406             return false;
20407         },
20408
20409         /**
20410          * My goal is to be able to transparently determine if an object is
20411          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20412          * returns "object", oDD.constructor.toString() always returns
20413          * "DragDrop" and not the name of the subclass.  So for now it just
20414          * evaluates a well-known variable in DragDrop.
20415          * @method isTypeOfDD
20416          * @param {Object} the object to evaluate
20417          * @return {boolean} true if typeof oDD = DragDrop
20418          * @static
20419          */
20420         isTypeOfDD: function (oDD) {
20421             return (oDD && oDD.__ygDragDrop);
20422         },
20423
20424         /**
20425          * Utility function to determine if a given element has been
20426          * registered as a drag drop handle for the given Drag Drop object.
20427          * @method isHandle
20428          * @param {String} id the element id to check
20429          * @return {boolean} true if this element is a DragDrop handle, false
20430          * otherwise
20431          * @static
20432          */
20433         isHandle: function(sDDId, sHandleId) {
20434             return ( this.handleIds[sDDId] &&
20435                             this.handleIds[sDDId][sHandleId] );
20436         },
20437
20438         /**
20439          * Returns the DragDrop instance for a given id
20440          * @method getDDById
20441          * @param {String} id the id of the DragDrop object
20442          * @return {DragDrop} the drag drop object, null if it is not found
20443          * @static
20444          */
20445         getDDById: function(id) {
20446             for (var i in this.ids) {
20447                 if (this.ids[i][id]) {
20448                     return this.ids[i][id];
20449                 }
20450             }
20451             return null;
20452         },
20453
20454         /**
20455          * Fired after a registered DragDrop object gets the mousedown event.
20456          * Sets up the events required to track the object being dragged
20457          * @method handleMouseDown
20458          * @param {Event} e the event
20459          * @param oDD the DragDrop object being dragged
20460          * @private
20461          * @static
20462          */
20463         handleMouseDown: function(e, oDD) {
20464             if(Roo.QuickTips){
20465                 Roo.QuickTips.disable();
20466             }
20467             this.currentTarget = e.getTarget();
20468
20469             this.dragCurrent = oDD;
20470
20471             var el = oDD.getEl();
20472
20473             // track start position
20474             this.startX = e.getPageX();
20475             this.startY = e.getPageY();
20476
20477             this.deltaX = this.startX - el.offsetLeft;
20478             this.deltaY = this.startY - el.offsetTop;
20479
20480             this.dragThreshMet = false;
20481
20482             this.clickTimeout = setTimeout(
20483                     function() {
20484                         var DDM = Roo.dd.DDM;
20485                         DDM.startDrag(DDM.startX, DDM.startY);
20486                     },
20487                     this.clickTimeThresh );
20488         },
20489
20490         /**
20491          * Fired when either the drag pixel threshol or the mousedown hold
20492          * time threshold has been met.
20493          * @method startDrag
20494          * @param x {int} the X position of the original mousedown
20495          * @param y {int} the Y position of the original mousedown
20496          * @static
20497          */
20498         startDrag: function(x, y) {
20499             clearTimeout(this.clickTimeout);
20500             if (this.dragCurrent) {
20501                 this.dragCurrent.b4StartDrag(x, y);
20502                 this.dragCurrent.startDrag(x, y);
20503             }
20504             this.dragThreshMet = true;
20505         },
20506
20507         /**
20508          * Internal function to handle the mouseup event.  Will be invoked
20509          * from the context of the document.
20510          * @method handleMouseUp
20511          * @param {Event} e the event
20512          * @private
20513          * @static
20514          */
20515         handleMouseUp: function(e) {
20516
20517             if(Roo.QuickTips){
20518                 Roo.QuickTips.enable();
20519             }
20520             if (! this.dragCurrent) {
20521                 return;
20522             }
20523
20524             clearTimeout(this.clickTimeout);
20525
20526             if (this.dragThreshMet) {
20527                 this.fireEvents(e, true);
20528             } else {
20529             }
20530
20531             this.stopDrag(e);
20532
20533             this.stopEvent(e);
20534         },
20535
20536         /**
20537          * Utility to stop event propagation and event default, if these
20538          * features are turned on.
20539          * @method stopEvent
20540          * @param {Event} e the event as returned by this.getEvent()
20541          * @static
20542          */
20543         stopEvent: function(e){
20544             if(this.stopPropagation) {
20545                 e.stopPropagation();
20546             }
20547
20548             if (this.preventDefault) {
20549                 e.preventDefault();
20550             }
20551         },
20552
20553         /**
20554          * Internal function to clean up event handlers after the drag
20555          * operation is complete
20556          * @method stopDrag
20557          * @param {Event} e the event
20558          * @private
20559          * @static
20560          */
20561         stopDrag: function(e) {
20562             // Fire the drag end event for the item that was dragged
20563             if (this.dragCurrent) {
20564                 if (this.dragThreshMet) {
20565                     this.dragCurrent.b4EndDrag(e);
20566                     this.dragCurrent.endDrag(e);
20567                 }
20568
20569                 this.dragCurrent.onMouseUp(e);
20570             }
20571
20572             this.dragCurrent = null;
20573             this.dragOvers = {};
20574         },
20575
20576         /**
20577          * Internal function to handle the mousemove event.  Will be invoked
20578          * from the context of the html element.
20579          *
20580          * @TODO figure out what we can do about mouse events lost when the
20581          * user drags objects beyond the window boundary.  Currently we can
20582          * detect this in internet explorer by verifying that the mouse is
20583          * down during the mousemove event.  Firefox doesn't give us the
20584          * button state on the mousemove event.
20585          * @method handleMouseMove
20586          * @param {Event} e the event
20587          * @private
20588          * @static
20589          */
20590         handleMouseMove: function(e) {
20591             if (! this.dragCurrent) {
20592                 return true;
20593             }
20594
20595             // var button = e.which || e.button;
20596
20597             // check for IE mouseup outside of page boundary
20598             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20599                 this.stopEvent(e);
20600                 return this.handleMouseUp(e);
20601             }
20602
20603             if (!this.dragThreshMet) {
20604                 var diffX = Math.abs(this.startX - e.getPageX());
20605                 var diffY = Math.abs(this.startY - e.getPageY());
20606                 if (diffX > this.clickPixelThresh ||
20607                             diffY > this.clickPixelThresh) {
20608                     this.startDrag(this.startX, this.startY);
20609                 }
20610             }
20611
20612             if (this.dragThreshMet) {
20613                 this.dragCurrent.b4Drag(e);
20614                 this.dragCurrent.onDrag(e);
20615                 if(!this.dragCurrent.moveOnly){
20616                     this.fireEvents(e, false);
20617                 }
20618             }
20619
20620             this.stopEvent(e);
20621
20622             return true;
20623         },
20624
20625         /**
20626          * Iterates over all of the DragDrop elements to find ones we are
20627          * hovering over or dropping on
20628          * @method fireEvents
20629          * @param {Event} e the event
20630          * @param {boolean} isDrop is this a drop op or a mouseover op?
20631          * @private
20632          * @static
20633          */
20634         fireEvents: function(e, isDrop) {
20635             var dc = this.dragCurrent;
20636
20637             // If the user did the mouse up outside of the window, we could
20638             // get here even though we have ended the drag.
20639             if (!dc || dc.isLocked()) {
20640                 return;
20641             }
20642
20643             var pt = e.getPoint();
20644
20645             // cache the previous dragOver array
20646             var oldOvers = [];
20647
20648             var outEvts   = [];
20649             var overEvts  = [];
20650             var dropEvts  = [];
20651             var enterEvts = [];
20652
20653             // Check to see if the object(s) we were hovering over is no longer
20654             // being hovered over so we can fire the onDragOut event
20655             for (var i in this.dragOvers) {
20656
20657                 var ddo = this.dragOvers[i];
20658
20659                 if (! this.isTypeOfDD(ddo)) {
20660                     continue;
20661                 }
20662
20663                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20664                     outEvts.push( ddo );
20665                 }
20666
20667                 oldOvers[i] = true;
20668                 delete this.dragOvers[i];
20669             }
20670
20671             for (var sGroup in dc.groups) {
20672
20673                 if ("string" != typeof sGroup) {
20674                     continue;
20675                 }
20676
20677                 for (i in this.ids[sGroup]) {
20678                     var oDD = this.ids[sGroup][i];
20679                     if (! this.isTypeOfDD(oDD)) {
20680                         continue;
20681                     }
20682
20683                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20684                         if (this.isOverTarget(pt, oDD, this.mode)) {
20685                             // look for drop interactions
20686                             if (isDrop) {
20687                                 dropEvts.push( oDD );
20688                             // look for drag enter and drag over interactions
20689                             } else {
20690
20691                                 // initial drag over: dragEnter fires
20692                                 if (!oldOvers[oDD.id]) {
20693                                     enterEvts.push( oDD );
20694                                 // subsequent drag overs: dragOver fires
20695                                 } else {
20696                                     overEvts.push( oDD );
20697                                 }
20698
20699                                 this.dragOvers[oDD.id] = oDD;
20700                             }
20701                         }
20702                     }
20703                 }
20704             }
20705
20706             if (this.mode) {
20707                 if (outEvts.length) {
20708                     dc.b4DragOut(e, outEvts);
20709                     dc.onDragOut(e, outEvts);
20710                 }
20711
20712                 if (enterEvts.length) {
20713                     dc.onDragEnter(e, enterEvts);
20714                 }
20715
20716                 if (overEvts.length) {
20717                     dc.b4DragOver(e, overEvts);
20718                     dc.onDragOver(e, overEvts);
20719                 }
20720
20721                 if (dropEvts.length) {
20722                     dc.b4DragDrop(e, dropEvts);
20723                     dc.onDragDrop(e, dropEvts);
20724                 }
20725
20726             } else {
20727                 // fire dragout events
20728                 var len = 0;
20729                 for (i=0, len=outEvts.length; i<len; ++i) {
20730                     dc.b4DragOut(e, outEvts[i].id);
20731                     dc.onDragOut(e, outEvts[i].id);
20732                 }
20733
20734                 // fire enter events
20735                 for (i=0,len=enterEvts.length; i<len; ++i) {
20736                     // dc.b4DragEnter(e, oDD.id);
20737                     dc.onDragEnter(e, enterEvts[i].id);
20738                 }
20739
20740                 // fire over events
20741                 for (i=0,len=overEvts.length; i<len; ++i) {
20742                     dc.b4DragOver(e, overEvts[i].id);
20743                     dc.onDragOver(e, overEvts[i].id);
20744                 }
20745
20746                 // fire drop events
20747                 for (i=0, len=dropEvts.length; i<len; ++i) {
20748                     dc.b4DragDrop(e, dropEvts[i].id);
20749                     dc.onDragDrop(e, dropEvts[i].id);
20750                 }
20751
20752             }
20753
20754             // notify about a drop that did not find a target
20755             if (isDrop && !dropEvts.length) {
20756                 dc.onInvalidDrop(e);
20757             }
20758
20759         },
20760
20761         /**
20762          * Helper function for getting the best match from the list of drag
20763          * and drop objects returned by the drag and drop events when we are
20764          * in INTERSECT mode.  It returns either the first object that the
20765          * cursor is over, or the object that has the greatest overlap with
20766          * the dragged element.
20767          * @method getBestMatch
20768          * @param  {DragDrop[]} dds The array of drag and drop objects
20769          * targeted
20770          * @return {DragDrop}       The best single match
20771          * @static
20772          */
20773         getBestMatch: function(dds) {
20774             var winner = null;
20775             // Return null if the input is not what we expect
20776             //if (!dds || !dds.length || dds.length == 0) {
20777                // winner = null;
20778             // If there is only one item, it wins
20779             //} else if (dds.length == 1) {
20780
20781             var len = dds.length;
20782
20783             if (len == 1) {
20784                 winner = dds[0];
20785             } else {
20786                 // Loop through the targeted items
20787                 for (var i=0; i<len; ++i) {
20788                     var dd = dds[i];
20789                     // If the cursor is over the object, it wins.  If the
20790                     // cursor is over multiple matches, the first one we come
20791                     // to wins.
20792                     if (dd.cursorIsOver) {
20793                         winner = dd;
20794                         break;
20795                     // Otherwise the object with the most overlap wins
20796                     } else {
20797                         if (!winner ||
20798                             winner.overlap.getArea() < dd.overlap.getArea()) {
20799                             winner = dd;
20800                         }
20801                     }
20802                 }
20803             }
20804
20805             return winner;
20806         },
20807
20808         /**
20809          * Refreshes the cache of the top-left and bottom-right points of the
20810          * drag and drop objects in the specified group(s).  This is in the
20811          * format that is stored in the drag and drop instance, so typical
20812          * usage is:
20813          * <code>
20814          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20815          * </code>
20816          * Alternatively:
20817          * <code>
20818          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20819          * </code>
20820          * @TODO this really should be an indexed array.  Alternatively this
20821          * method could accept both.
20822          * @method refreshCache
20823          * @param {Object} groups an associative array of groups to refresh
20824          * @static
20825          */
20826         refreshCache: function(groups) {
20827             for (var sGroup in groups) {
20828                 if ("string" != typeof sGroup) {
20829                     continue;
20830                 }
20831                 for (var i in this.ids[sGroup]) {
20832                     var oDD = this.ids[sGroup][i];
20833
20834                     if (this.isTypeOfDD(oDD)) {
20835                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20836                         var loc = this.getLocation(oDD);
20837                         if (loc) {
20838                             this.locationCache[oDD.id] = loc;
20839                         } else {
20840                             delete this.locationCache[oDD.id];
20841                             // this will unregister the drag and drop object if
20842                             // the element is not in a usable state
20843                             // oDD.unreg();
20844                         }
20845                     }
20846                 }
20847             }
20848         },
20849
20850         /**
20851          * This checks to make sure an element exists and is in the DOM.  The
20852          * main purpose is to handle cases where innerHTML is used to remove
20853          * drag and drop objects from the DOM.  IE provides an 'unspecified
20854          * error' when trying to access the offsetParent of such an element
20855          * @method verifyEl
20856          * @param {HTMLElement} el the element to check
20857          * @return {boolean} true if the element looks usable
20858          * @static
20859          */
20860         verifyEl: function(el) {
20861             if (el) {
20862                 var parent;
20863                 if(Roo.isIE){
20864                     try{
20865                         parent = el.offsetParent;
20866                     }catch(e){}
20867                 }else{
20868                     parent = el.offsetParent;
20869                 }
20870                 if (parent) {
20871                     return true;
20872                 }
20873             }
20874
20875             return false;
20876         },
20877
20878         /**
20879          * Returns a Region object containing the drag and drop element's position
20880          * and size, including the padding configured for it
20881          * @method getLocation
20882          * @param {DragDrop} oDD the drag and drop object to get the
20883          *                       location for
20884          * @return {Roo.lib.Region} a Region object representing the total area
20885          *                             the element occupies, including any padding
20886          *                             the instance is configured for.
20887          * @static
20888          */
20889         getLocation: function(oDD) {
20890             if (! this.isTypeOfDD(oDD)) {
20891                 return null;
20892             }
20893
20894             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20895
20896             try {
20897                 pos= Roo.lib.Dom.getXY(el);
20898             } catch (e) { }
20899
20900             if (!pos) {
20901                 return null;
20902             }
20903
20904             x1 = pos[0];
20905             x2 = x1 + el.offsetWidth;
20906             y1 = pos[1];
20907             y2 = y1 + el.offsetHeight;
20908
20909             t = y1 - oDD.padding[0];
20910             r = x2 + oDD.padding[1];
20911             b = y2 + oDD.padding[2];
20912             l = x1 - oDD.padding[3];
20913
20914             return new Roo.lib.Region( t, r, b, l );
20915         },
20916
20917         /**
20918          * Checks the cursor location to see if it over the target
20919          * @method isOverTarget
20920          * @param {Roo.lib.Point} pt The point to evaluate
20921          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20922          * @return {boolean} true if the mouse is over the target
20923          * @private
20924          * @static
20925          */
20926         isOverTarget: function(pt, oTarget, intersect) {
20927             // use cache if available
20928             var loc = this.locationCache[oTarget.id];
20929             if (!loc || !this.useCache) {
20930                 loc = this.getLocation(oTarget);
20931                 this.locationCache[oTarget.id] = loc;
20932
20933             }
20934
20935             if (!loc) {
20936                 return false;
20937             }
20938
20939             oTarget.cursorIsOver = loc.contains( pt );
20940
20941             // DragDrop is using this as a sanity check for the initial mousedown
20942             // in this case we are done.  In POINT mode, if the drag obj has no
20943             // contraints, we are also done. Otherwise we need to evaluate the
20944             // location of the target as related to the actual location of the
20945             // dragged element.
20946             var dc = this.dragCurrent;
20947             if (!dc || !dc.getTargetCoord ||
20948                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20949                 return oTarget.cursorIsOver;
20950             }
20951
20952             oTarget.overlap = null;
20953
20954             // Get the current location of the drag element, this is the
20955             // location of the mouse event less the delta that represents
20956             // where the original mousedown happened on the element.  We
20957             // need to consider constraints and ticks as well.
20958             var pos = dc.getTargetCoord(pt.x, pt.y);
20959
20960             var el = dc.getDragEl();
20961             var curRegion = new Roo.lib.Region( pos.y,
20962                                                    pos.x + el.offsetWidth,
20963                                                    pos.y + el.offsetHeight,
20964                                                    pos.x );
20965
20966             var overlap = curRegion.intersect(loc);
20967
20968             if (overlap) {
20969                 oTarget.overlap = overlap;
20970                 return (intersect) ? true : oTarget.cursorIsOver;
20971             } else {
20972                 return false;
20973             }
20974         },
20975
20976         /**
20977          * unload event handler
20978          * @method _onUnload
20979          * @private
20980          * @static
20981          */
20982         _onUnload: function(e, me) {
20983             Roo.dd.DragDropMgr.unregAll();
20984         },
20985
20986         /**
20987          * Cleans up the drag and drop events and objects.
20988          * @method unregAll
20989          * @private
20990          * @static
20991          */
20992         unregAll: function() {
20993
20994             if (this.dragCurrent) {
20995                 this.stopDrag();
20996                 this.dragCurrent = null;
20997             }
20998
20999             this._execOnAll("unreg", []);
21000
21001             for (i in this.elementCache) {
21002                 delete this.elementCache[i];
21003             }
21004
21005             this.elementCache = {};
21006             this.ids = {};
21007         },
21008
21009         /**
21010          * A cache of DOM elements
21011          * @property elementCache
21012          * @private
21013          * @static
21014          */
21015         elementCache: {},
21016
21017         /**
21018          * Get the wrapper for the DOM element specified
21019          * @method getElWrapper
21020          * @param {String} id the id of the element to get
21021          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21022          * @private
21023          * @deprecated This wrapper isn't that useful
21024          * @static
21025          */
21026         getElWrapper: function(id) {
21027             var oWrapper = this.elementCache[id];
21028             if (!oWrapper || !oWrapper.el) {
21029                 oWrapper = this.elementCache[id] =
21030                     new this.ElementWrapper(Roo.getDom(id));
21031             }
21032             return oWrapper;
21033         },
21034
21035         /**
21036          * Returns the actual DOM element
21037          * @method getElement
21038          * @param {String} id the id of the elment to get
21039          * @return {Object} The element
21040          * @deprecated use Roo.getDom instead
21041          * @static
21042          */
21043         getElement: function(id) {
21044             return Roo.getDom(id);
21045         },
21046
21047         /**
21048          * Returns the style property for the DOM element (i.e.,
21049          * document.getElById(id).style)
21050          * @method getCss
21051          * @param {String} id the id of the elment to get
21052          * @return {Object} The style property of the element
21053          * @deprecated use Roo.getDom instead
21054          * @static
21055          */
21056         getCss: function(id) {
21057             var el = Roo.getDom(id);
21058             return (el) ? el.style : null;
21059         },
21060
21061         /**
21062          * Inner class for cached elements
21063          * @class DragDropMgr.ElementWrapper
21064          * @for DragDropMgr
21065          * @private
21066          * @deprecated
21067          */
21068         ElementWrapper: function(el) {
21069                 /**
21070                  * The element
21071                  * @property el
21072                  */
21073                 this.el = el || null;
21074                 /**
21075                  * The element id
21076                  * @property id
21077                  */
21078                 this.id = this.el && el.id;
21079                 /**
21080                  * A reference to the style property
21081                  * @property css
21082                  */
21083                 this.css = this.el && el.style;
21084             },
21085
21086         /**
21087          * Returns the X position of an html element
21088          * @method getPosX
21089          * @param el the element for which to get the position
21090          * @return {int} the X coordinate
21091          * @for DragDropMgr
21092          * @deprecated use Roo.lib.Dom.getX instead
21093          * @static
21094          */
21095         getPosX: function(el) {
21096             return Roo.lib.Dom.getX(el);
21097         },
21098
21099         /**
21100          * Returns the Y position of an html element
21101          * @method getPosY
21102          * @param el the element for which to get the position
21103          * @return {int} the Y coordinate
21104          * @deprecated use Roo.lib.Dom.getY instead
21105          * @static
21106          */
21107         getPosY: function(el) {
21108             return Roo.lib.Dom.getY(el);
21109         },
21110
21111         /**
21112          * Swap two nodes.  In IE, we use the native method, for others we
21113          * emulate the IE behavior
21114          * @method swapNode
21115          * @param n1 the first node to swap
21116          * @param n2 the other node to swap
21117          * @static
21118          */
21119         swapNode: function(n1, n2) {
21120             if (n1.swapNode) {
21121                 n1.swapNode(n2);
21122             } else {
21123                 var p = n2.parentNode;
21124                 var s = n2.nextSibling;
21125
21126                 if (s == n1) {
21127                     p.insertBefore(n1, n2);
21128                 } else if (n2 == n1.nextSibling) {
21129                     p.insertBefore(n2, n1);
21130                 } else {
21131                     n1.parentNode.replaceChild(n2, n1);
21132                     p.insertBefore(n1, s);
21133                 }
21134             }
21135         },
21136
21137         /**
21138          * Returns the current scroll position
21139          * @method getScroll
21140          * @private
21141          * @static
21142          */
21143         getScroll: function () {
21144             var t, l, dde=document.documentElement, db=document.body;
21145             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21146                 t = dde.scrollTop;
21147                 l = dde.scrollLeft;
21148             } else if (db) {
21149                 t = db.scrollTop;
21150                 l = db.scrollLeft;
21151             } else {
21152
21153             }
21154             return { top: t, left: l };
21155         },
21156
21157         /**
21158          * Returns the specified element style property
21159          * @method getStyle
21160          * @param {HTMLElement} el          the element
21161          * @param {string}      styleProp   the style property
21162          * @return {string} The value of the style property
21163          * @deprecated use Roo.lib.Dom.getStyle
21164          * @static
21165          */
21166         getStyle: function(el, styleProp) {
21167             return Roo.fly(el).getStyle(styleProp);
21168         },
21169
21170         /**
21171          * Gets the scrollTop
21172          * @method getScrollTop
21173          * @return {int} the document's scrollTop
21174          * @static
21175          */
21176         getScrollTop: function () { return this.getScroll().top; },
21177
21178         /**
21179          * Gets the scrollLeft
21180          * @method getScrollLeft
21181          * @return {int} the document's scrollTop
21182          * @static
21183          */
21184         getScrollLeft: function () { return this.getScroll().left; },
21185
21186         /**
21187          * Sets the x/y position of an element to the location of the
21188          * target element.
21189          * @method moveToEl
21190          * @param {HTMLElement} moveEl      The element to move
21191          * @param {HTMLElement} targetEl    The position reference element
21192          * @static
21193          */
21194         moveToEl: function (moveEl, targetEl) {
21195             var aCoord = Roo.lib.Dom.getXY(targetEl);
21196             Roo.lib.Dom.setXY(moveEl, aCoord);
21197         },
21198
21199         /**
21200          * Numeric array sort function
21201          * @method numericSort
21202          * @static
21203          */
21204         numericSort: function(a, b) { return (a - b); },
21205
21206         /**
21207          * Internal counter
21208          * @property _timeoutCount
21209          * @private
21210          * @static
21211          */
21212         _timeoutCount: 0,
21213
21214         /**
21215          * Trying to make the load order less important.  Without this we get
21216          * an error if this file is loaded before the Event Utility.
21217          * @method _addListeners
21218          * @private
21219          * @static
21220          */
21221         _addListeners: function() {
21222             var DDM = Roo.dd.DDM;
21223             if ( Roo.lib.Event && document ) {
21224                 DDM._onLoad();
21225             } else {
21226                 if (DDM._timeoutCount > 2000) {
21227                 } else {
21228                     setTimeout(DDM._addListeners, 10);
21229                     if (document && document.body) {
21230                         DDM._timeoutCount += 1;
21231                     }
21232                 }
21233             }
21234         },
21235
21236         /**
21237          * Recursively searches the immediate parent and all child nodes for
21238          * the handle element in order to determine wheter or not it was
21239          * clicked.
21240          * @method handleWasClicked
21241          * @param node the html element to inspect
21242          * @static
21243          */
21244         handleWasClicked: function(node, id) {
21245             if (this.isHandle(id, node.id)) {
21246                 return true;
21247             } else {
21248                 // check to see if this is a text node child of the one we want
21249                 var p = node.parentNode;
21250
21251                 while (p) {
21252                     if (this.isHandle(id, p.id)) {
21253                         return true;
21254                     } else {
21255                         p = p.parentNode;
21256                     }
21257                 }
21258             }
21259
21260             return false;
21261         }
21262
21263     };
21264
21265 }();
21266
21267 // shorter alias, save a few bytes
21268 Roo.dd.DDM = Roo.dd.DragDropMgr;
21269 Roo.dd.DDM._addListeners();
21270
21271 }/*
21272  * Based on:
21273  * Ext JS Library 1.1.1
21274  * Copyright(c) 2006-2007, Ext JS, LLC.
21275  *
21276  * Originally Released Under LGPL - original licence link has changed is not relivant.
21277  *
21278  * Fork - LGPL
21279  * <script type="text/javascript">
21280  */
21281
21282 /**
21283  * @class Roo.dd.DD
21284  * A DragDrop implementation where the linked element follows the
21285  * mouse cursor during a drag.
21286  * @extends Roo.dd.DragDrop
21287  * @constructor
21288  * @param {String} id the id of the linked element
21289  * @param {String} sGroup the group of related DragDrop items
21290  * @param {object} config an object containing configurable attributes
21291  *                Valid properties for DD:
21292  *                    scroll
21293  */
21294 Roo.dd.DD = function(id, sGroup, config) {
21295     if (id) {
21296         this.init(id, sGroup, config);
21297     }
21298 };
21299
21300 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21301
21302     /**
21303      * When set to true, the utility automatically tries to scroll the browser
21304      * window wehn a drag and drop element is dragged near the viewport boundary.
21305      * Defaults to true.
21306      * @property scroll
21307      * @type boolean
21308      */
21309     scroll: true,
21310
21311     /**
21312      * Sets the pointer offset to the distance between the linked element's top
21313      * left corner and the location the element was clicked
21314      * @method autoOffset
21315      * @param {int} iPageX the X coordinate of the click
21316      * @param {int} iPageY the Y coordinate of the click
21317      */
21318     autoOffset: function(iPageX, iPageY) {
21319         var x = iPageX - this.startPageX;
21320         var y = iPageY - this.startPageY;
21321         this.setDelta(x, y);
21322     },
21323
21324     /**
21325      * Sets the pointer offset.  You can call this directly to force the
21326      * offset to be in a particular location (e.g., pass in 0,0 to set it
21327      * to the center of the object)
21328      * @method setDelta
21329      * @param {int} iDeltaX the distance from the left
21330      * @param {int} iDeltaY the distance from the top
21331      */
21332     setDelta: function(iDeltaX, iDeltaY) {
21333         this.deltaX = iDeltaX;
21334         this.deltaY = iDeltaY;
21335     },
21336
21337     /**
21338      * Sets the drag element to the location of the mousedown or click event,
21339      * maintaining the cursor location relative to the location on the element
21340      * that was clicked.  Override this if you want to place the element in a
21341      * location other than where the cursor is.
21342      * @method setDragElPos
21343      * @param {int} iPageX the X coordinate of the mousedown or drag event
21344      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21345      */
21346     setDragElPos: function(iPageX, iPageY) {
21347         // the first time we do this, we are going to check to make sure
21348         // the element has css positioning
21349
21350         var el = this.getDragEl();
21351         this.alignElWithMouse(el, iPageX, iPageY);
21352     },
21353
21354     /**
21355      * Sets the element to the location of the mousedown or click event,
21356      * maintaining the cursor location relative to the location on the element
21357      * that was clicked.  Override this if you want to place the element in a
21358      * location other than where the cursor is.
21359      * @method alignElWithMouse
21360      * @param {HTMLElement} el the element to move
21361      * @param {int} iPageX the X coordinate of the mousedown or drag event
21362      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21363      */
21364     alignElWithMouse: function(el, iPageX, iPageY) {
21365         var oCoord = this.getTargetCoord(iPageX, iPageY);
21366         var fly = el.dom ? el : Roo.fly(el);
21367         if (!this.deltaSetXY) {
21368             var aCoord = [oCoord.x, oCoord.y];
21369             fly.setXY(aCoord);
21370             var newLeft = fly.getLeft(true);
21371             var newTop  = fly.getTop(true);
21372             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21373         } else {
21374             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21375         }
21376
21377         this.cachePosition(oCoord.x, oCoord.y);
21378         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21379         return oCoord;
21380     },
21381
21382     /**
21383      * Saves the most recent position so that we can reset the constraints and
21384      * tick marks on-demand.  We need to know this so that we can calculate the
21385      * number of pixels the element is offset from its original position.
21386      * @method cachePosition
21387      * @param iPageX the current x position (optional, this just makes it so we
21388      * don't have to look it up again)
21389      * @param iPageY the current y position (optional, this just makes it so we
21390      * don't have to look it up again)
21391      */
21392     cachePosition: function(iPageX, iPageY) {
21393         if (iPageX) {
21394             this.lastPageX = iPageX;
21395             this.lastPageY = iPageY;
21396         } else {
21397             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21398             this.lastPageX = aCoord[0];
21399             this.lastPageY = aCoord[1];
21400         }
21401     },
21402
21403     /**
21404      * Auto-scroll the window if the dragged object has been moved beyond the
21405      * visible window boundary.
21406      * @method autoScroll
21407      * @param {int} x the drag element's x position
21408      * @param {int} y the drag element's y position
21409      * @param {int} h the height of the drag element
21410      * @param {int} w the width of the drag element
21411      * @private
21412      */
21413     autoScroll: function(x, y, h, w) {
21414
21415         if (this.scroll) {
21416             // The client height
21417             var clientH = Roo.lib.Dom.getViewWidth();
21418
21419             // The client width
21420             var clientW = Roo.lib.Dom.getViewHeight();
21421
21422             // The amt scrolled down
21423             var st = this.DDM.getScrollTop();
21424
21425             // The amt scrolled right
21426             var sl = this.DDM.getScrollLeft();
21427
21428             // Location of the bottom of the element
21429             var bot = h + y;
21430
21431             // Location of the right of the element
21432             var right = w + x;
21433
21434             // The distance from the cursor to the bottom of the visible area,
21435             // adjusted so that we don't scroll if the cursor is beyond the
21436             // element drag constraints
21437             var toBot = (clientH + st - y - this.deltaY);
21438
21439             // The distance from the cursor to the right of the visible area
21440             var toRight = (clientW + sl - x - this.deltaX);
21441
21442
21443             // How close to the edge the cursor must be before we scroll
21444             // var thresh = (document.all) ? 100 : 40;
21445             var thresh = 40;
21446
21447             // How many pixels to scroll per autoscroll op.  This helps to reduce
21448             // clunky scrolling. IE is more sensitive about this ... it needs this
21449             // value to be higher.
21450             var scrAmt = (document.all) ? 80 : 30;
21451
21452             // Scroll down if we are near the bottom of the visible page and the
21453             // obj extends below the crease
21454             if ( bot > clientH && toBot < thresh ) {
21455                 window.scrollTo(sl, st + scrAmt);
21456             }
21457
21458             // Scroll up if the window is scrolled down and the top of the object
21459             // goes above the top border
21460             if ( y < st && st > 0 && y - st < thresh ) {
21461                 window.scrollTo(sl, st - scrAmt);
21462             }
21463
21464             // Scroll right if the obj is beyond the right border and the cursor is
21465             // near the border.
21466             if ( right > clientW && toRight < thresh ) {
21467                 window.scrollTo(sl + scrAmt, st);
21468             }
21469
21470             // Scroll left if the window has been scrolled to the right and the obj
21471             // extends past the left border
21472             if ( x < sl && sl > 0 && x - sl < thresh ) {
21473                 window.scrollTo(sl - scrAmt, st);
21474             }
21475         }
21476     },
21477
21478     /**
21479      * Finds the location the element should be placed if we want to move
21480      * it to where the mouse location less the click offset would place us.
21481      * @method getTargetCoord
21482      * @param {int} iPageX the X coordinate of the click
21483      * @param {int} iPageY the Y coordinate of the click
21484      * @return an object that contains the coordinates (Object.x and Object.y)
21485      * @private
21486      */
21487     getTargetCoord: function(iPageX, iPageY) {
21488
21489
21490         var x = iPageX - this.deltaX;
21491         var y = iPageY - this.deltaY;
21492
21493         if (this.constrainX) {
21494             if (x < this.minX) { x = this.minX; }
21495             if (x > this.maxX) { x = this.maxX; }
21496         }
21497
21498         if (this.constrainY) {
21499             if (y < this.minY) { y = this.minY; }
21500             if (y > this.maxY) { y = this.maxY; }
21501         }
21502
21503         x = this.getTick(x, this.xTicks);
21504         y = this.getTick(y, this.yTicks);
21505
21506
21507         return {x:x, y:y};
21508     },
21509
21510     /*
21511      * Sets up config options specific to this class. Overrides
21512      * Roo.dd.DragDrop, but all versions of this method through the
21513      * inheritance chain are called
21514      */
21515     applyConfig: function() {
21516         Roo.dd.DD.superclass.applyConfig.call(this);
21517         this.scroll = (this.config.scroll !== false);
21518     },
21519
21520     /*
21521      * Event that fires prior to the onMouseDown event.  Overrides
21522      * Roo.dd.DragDrop.
21523      */
21524     b4MouseDown: function(e) {
21525         // this.resetConstraints();
21526         this.autoOffset(e.getPageX(),
21527                             e.getPageY());
21528     },
21529
21530     /*
21531      * Event that fires prior to the onDrag event.  Overrides
21532      * Roo.dd.DragDrop.
21533      */
21534     b4Drag: function(e) {
21535         this.setDragElPos(e.getPageX(),
21536                             e.getPageY());
21537     },
21538
21539     toString: function() {
21540         return ("DD " + this.id);
21541     }
21542
21543     //////////////////////////////////////////////////////////////////////////
21544     // Debugging ygDragDrop events that can be overridden
21545     //////////////////////////////////////////////////////////////////////////
21546     /*
21547     startDrag: function(x, y) {
21548     },
21549
21550     onDrag: function(e) {
21551     },
21552
21553     onDragEnter: function(e, id) {
21554     },
21555
21556     onDragOver: function(e, id) {
21557     },
21558
21559     onDragOut: function(e, id) {
21560     },
21561
21562     onDragDrop: function(e, id) {
21563     },
21564
21565     endDrag: function(e) {
21566     }
21567
21568     */
21569
21570 });/*
21571  * Based on:
21572  * Ext JS Library 1.1.1
21573  * Copyright(c) 2006-2007, Ext JS, LLC.
21574  *
21575  * Originally Released Under LGPL - original licence link has changed is not relivant.
21576  *
21577  * Fork - LGPL
21578  * <script type="text/javascript">
21579  */
21580
21581 /**
21582  * @class Roo.dd.DDProxy
21583  * A DragDrop implementation that inserts an empty, bordered div into
21584  * the document that follows the cursor during drag operations.  At the time of
21585  * the click, the frame div is resized to the dimensions of the linked html
21586  * element, and moved to the exact location of the linked element.
21587  *
21588  * References to the "frame" element refer to the single proxy element that
21589  * was created to be dragged in place of all DDProxy elements on the
21590  * page.
21591  *
21592  * @extends Roo.dd.DD
21593  * @constructor
21594  * @param {String} id the id of the linked html element
21595  * @param {String} sGroup the group of related DragDrop objects
21596  * @param {object} config an object containing configurable attributes
21597  *                Valid properties for DDProxy in addition to those in DragDrop:
21598  *                   resizeFrame, centerFrame, dragElId
21599  */
21600 Roo.dd.DDProxy = function(id, sGroup, config) {
21601     if (id) {
21602         this.init(id, sGroup, config);
21603         this.initFrame();
21604     }
21605 };
21606
21607 /**
21608  * The default drag frame div id
21609  * @property Roo.dd.DDProxy.dragElId
21610  * @type String
21611  * @static
21612  */
21613 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21614
21615 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21616
21617     /**
21618      * By default we resize the drag frame to be the same size as the element
21619      * we want to drag (this is to get the frame effect).  We can turn it off
21620      * if we want a different behavior.
21621      * @property resizeFrame
21622      * @type boolean
21623      */
21624     resizeFrame: true,
21625
21626     /**
21627      * By default the frame is positioned exactly where the drag element is, so
21628      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21629      * you do not have constraints on the obj is to have the drag frame centered
21630      * around the cursor.  Set centerFrame to true for this effect.
21631      * @property centerFrame
21632      * @type boolean
21633      */
21634     centerFrame: false,
21635
21636     /**
21637      * Creates the proxy element if it does not yet exist
21638      * @method createFrame
21639      */
21640     createFrame: function() {
21641         var self = this;
21642         var body = document.body;
21643
21644         if (!body || !body.firstChild) {
21645             setTimeout( function() { self.createFrame(); }, 50 );
21646             return;
21647         }
21648
21649         var div = this.getDragEl();
21650
21651         if (!div) {
21652             div    = document.createElement("div");
21653             div.id = this.dragElId;
21654             var s  = div.style;
21655
21656             s.position   = "absolute";
21657             s.visibility = "hidden";
21658             s.cursor     = "move";
21659             s.border     = "2px solid #aaa";
21660             s.zIndex     = 999;
21661
21662             // appendChild can blow up IE if invoked prior to the window load event
21663             // while rendering a table.  It is possible there are other scenarios
21664             // that would cause this to happen as well.
21665             body.insertBefore(div, body.firstChild);
21666         }
21667     },
21668
21669     /**
21670      * Initialization for the drag frame element.  Must be called in the
21671      * constructor of all subclasses
21672      * @method initFrame
21673      */
21674     initFrame: function() {
21675         this.createFrame();
21676     },
21677
21678     applyConfig: function() {
21679         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21680
21681         this.resizeFrame = (this.config.resizeFrame !== false);
21682         this.centerFrame = (this.config.centerFrame);
21683         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21684     },
21685
21686     /**
21687      * Resizes the drag frame to the dimensions of the clicked object, positions
21688      * it over the object, and finally displays it
21689      * @method showFrame
21690      * @param {int} iPageX X click position
21691      * @param {int} iPageY Y click position
21692      * @private
21693      */
21694     showFrame: function(iPageX, iPageY) {
21695         var el = this.getEl();
21696         var dragEl = this.getDragEl();
21697         var s = dragEl.style;
21698
21699         this._resizeProxy();
21700
21701         if (this.centerFrame) {
21702             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21703                            Math.round(parseInt(s.height, 10)/2) );
21704         }
21705
21706         this.setDragElPos(iPageX, iPageY);
21707
21708         Roo.fly(dragEl).show();
21709     },
21710
21711     /**
21712      * The proxy is automatically resized to the dimensions of the linked
21713      * element when a drag is initiated, unless resizeFrame is set to false
21714      * @method _resizeProxy
21715      * @private
21716      */
21717     _resizeProxy: function() {
21718         if (this.resizeFrame) {
21719             var el = this.getEl();
21720             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21721         }
21722     },
21723
21724     // overrides Roo.dd.DragDrop
21725     b4MouseDown: function(e) {
21726         var x = e.getPageX();
21727         var y = e.getPageY();
21728         this.autoOffset(x, y);
21729         this.setDragElPos(x, y);
21730     },
21731
21732     // overrides Roo.dd.DragDrop
21733     b4StartDrag: function(x, y) {
21734         // show the drag frame
21735         this.showFrame(x, y);
21736     },
21737
21738     // overrides Roo.dd.DragDrop
21739     b4EndDrag: function(e) {
21740         Roo.fly(this.getDragEl()).hide();
21741     },
21742
21743     // overrides Roo.dd.DragDrop
21744     // By default we try to move the element to the last location of the frame.
21745     // This is so that the default behavior mirrors that of Roo.dd.DD.
21746     endDrag: function(e) {
21747
21748         var lel = this.getEl();
21749         var del = this.getDragEl();
21750
21751         // Show the drag frame briefly so we can get its position
21752         del.style.visibility = "";
21753
21754         this.beforeMove();
21755         // Hide the linked element before the move to get around a Safari
21756         // rendering bug.
21757         lel.style.visibility = "hidden";
21758         Roo.dd.DDM.moveToEl(lel, del);
21759         del.style.visibility = "hidden";
21760         lel.style.visibility = "";
21761
21762         this.afterDrag();
21763     },
21764
21765     beforeMove : function(){
21766
21767     },
21768
21769     afterDrag : function(){
21770
21771     },
21772
21773     toString: function() {
21774         return ("DDProxy " + this.id);
21775     }
21776
21777 });
21778 /*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788
21789  /**
21790  * @class Roo.dd.DDTarget
21791  * A DragDrop implementation that does not move, but can be a drop
21792  * target.  You would get the same result by simply omitting implementation
21793  * for the event callbacks, but this way we reduce the processing cost of the
21794  * event listener and the callbacks.
21795  * @extends Roo.dd.DragDrop
21796  * @constructor
21797  * @param {String} id the id of the element that is a drop target
21798  * @param {String} sGroup the group of related DragDrop objects
21799  * @param {object} config an object containing configurable attributes
21800  *                 Valid properties for DDTarget in addition to those in
21801  *                 DragDrop:
21802  *                    none
21803  */
21804 Roo.dd.DDTarget = function(id, sGroup, config) {
21805     if (id) {
21806         this.initTarget(id, sGroup, config);
21807     }
21808     if (config && (config.listeners || config.events)) { 
21809         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21810             listeners : config.listeners || {}, 
21811             events : config.events || {} 
21812         });    
21813     }
21814 };
21815
21816 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21817 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21818     toString: function() {
21819         return ("DDTarget " + this.id);
21820     }
21821 });
21822 /*
21823  * Based on:
21824  * Ext JS Library 1.1.1
21825  * Copyright(c) 2006-2007, Ext JS, LLC.
21826  *
21827  * Originally Released Under LGPL - original licence link has changed is not relivant.
21828  *
21829  * Fork - LGPL
21830  * <script type="text/javascript">
21831  */
21832  
21833
21834 /**
21835  * @class Roo.dd.ScrollManager
21836  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21837  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21838  * @singleton
21839  */
21840 Roo.dd.ScrollManager = function(){
21841     var ddm = Roo.dd.DragDropMgr;
21842     var els = {};
21843     var dragEl = null;
21844     var proc = {};
21845     
21846     
21847     
21848     var onStop = function(e){
21849         dragEl = null;
21850         clearProc();
21851     };
21852     
21853     var triggerRefresh = function(){
21854         if(ddm.dragCurrent){
21855              ddm.refreshCache(ddm.dragCurrent.groups);
21856         }
21857     };
21858     
21859     var doScroll = function(){
21860         if(ddm.dragCurrent){
21861             var dds = Roo.dd.ScrollManager;
21862             if(!dds.animate){
21863                 if(proc.el.scroll(proc.dir, dds.increment)){
21864                     triggerRefresh();
21865                 }
21866             }else{
21867                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21868             }
21869         }
21870     };
21871     
21872     var clearProc = function(){
21873         if(proc.id){
21874             clearInterval(proc.id);
21875         }
21876         proc.id = 0;
21877         proc.el = null;
21878         proc.dir = "";
21879     };
21880     
21881     var startProc = function(el, dir){
21882          Roo.log('scroll startproc');
21883         clearProc();
21884         proc.el = el;
21885         proc.dir = dir;
21886         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21887     };
21888     
21889     var onFire = function(e, isDrop){
21890        
21891         if(isDrop || !ddm.dragCurrent){ return; }
21892         var dds = Roo.dd.ScrollManager;
21893         if(!dragEl || dragEl != ddm.dragCurrent){
21894             dragEl = ddm.dragCurrent;
21895             // refresh regions on drag start
21896             dds.refreshCache();
21897         }
21898         
21899         var xy = Roo.lib.Event.getXY(e);
21900         var pt = new Roo.lib.Point(xy[0], xy[1]);
21901         for(var id in els){
21902             var el = els[id], r = el._region;
21903             if(r && r.contains(pt) && el.isScrollable()){
21904                 if(r.bottom - pt.y <= dds.thresh){
21905                     if(proc.el != el){
21906                         startProc(el, "down");
21907                     }
21908                     return;
21909                 }else if(r.right - pt.x <= dds.thresh){
21910                     if(proc.el != el){
21911                         startProc(el, "left");
21912                     }
21913                     return;
21914                 }else if(pt.y - r.top <= dds.thresh){
21915                     if(proc.el != el){
21916                         startProc(el, "up");
21917                     }
21918                     return;
21919                 }else if(pt.x - r.left <= dds.thresh){
21920                     if(proc.el != el){
21921                         startProc(el, "right");
21922                     }
21923                     return;
21924                 }
21925             }
21926         }
21927         clearProc();
21928     };
21929     
21930     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21931     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21932     
21933     return {
21934         /**
21935          * Registers new overflow element(s) to auto scroll
21936          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21937          */
21938         register : function(el){
21939             if(el instanceof Array){
21940                 for(var i = 0, len = el.length; i < len; i++) {
21941                         this.register(el[i]);
21942                 }
21943             }else{
21944                 el = Roo.get(el);
21945                 els[el.id] = el;
21946             }
21947             Roo.dd.ScrollManager.els = els;
21948         },
21949         
21950         /**
21951          * Unregisters overflow element(s) so they are no longer scrolled
21952          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21953          */
21954         unregister : function(el){
21955             if(el instanceof Array){
21956                 for(var i = 0, len = el.length; i < len; i++) {
21957                         this.unregister(el[i]);
21958                 }
21959             }else{
21960                 el = Roo.get(el);
21961                 delete els[el.id];
21962             }
21963         },
21964         
21965         /**
21966          * The number of pixels from the edge of a container the pointer needs to be to 
21967          * trigger scrolling (defaults to 25)
21968          * @type Number
21969          */
21970         thresh : 25,
21971         
21972         /**
21973          * The number of pixels to scroll in each scroll increment (defaults to 50)
21974          * @type Number
21975          */
21976         increment : 100,
21977         
21978         /**
21979          * The frequency of scrolls in milliseconds (defaults to 500)
21980          * @type Number
21981          */
21982         frequency : 500,
21983         
21984         /**
21985          * True to animate the scroll (defaults to true)
21986          * @type Boolean
21987          */
21988         animate: true,
21989         
21990         /**
21991          * The animation duration in seconds - 
21992          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21993          * @type Number
21994          */
21995         animDuration: .4,
21996         
21997         /**
21998          * Manually trigger a cache refresh.
21999          */
22000         refreshCache : function(){
22001             for(var id in els){
22002                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22003                     els[id]._region = els[id].getRegion();
22004                 }
22005             }
22006         }
22007     };
22008 }();/*
22009  * Based on:
22010  * Ext JS Library 1.1.1
22011  * Copyright(c) 2006-2007, Ext JS, LLC.
22012  *
22013  * Originally Released Under LGPL - original licence link has changed is not relivant.
22014  *
22015  * Fork - LGPL
22016  * <script type="text/javascript">
22017  */
22018  
22019
22020 /**
22021  * @class Roo.dd.Registry
22022  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22023  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22024  * @singleton
22025  */
22026 Roo.dd.Registry = function(){
22027     var elements = {}; 
22028     var handles = {}; 
22029     var autoIdSeed = 0;
22030
22031     var getId = function(el, autogen){
22032         if(typeof el == "string"){
22033             return el;
22034         }
22035         var id = el.id;
22036         if(!id && autogen !== false){
22037             id = "roodd-" + (++autoIdSeed);
22038             el.id = id;
22039         }
22040         return id;
22041     };
22042     
22043     return {
22044     /**
22045      * Register a drag drop element
22046      * @param {String|HTMLElement} element The id or DOM node to register
22047      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22048      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22049      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22050      * populated in the data object (if applicable):
22051      * <pre>
22052 Value      Description<br />
22053 ---------  ------------------------------------------<br />
22054 handles    Array of DOM nodes that trigger dragging<br />
22055            for the element being registered<br />
22056 isHandle   True if the element passed in triggers<br />
22057            dragging itself, else false
22058 </pre>
22059      */
22060         register : function(el, data){
22061             data = data || {};
22062             if(typeof el == "string"){
22063                 el = document.getElementById(el);
22064             }
22065             data.ddel = el;
22066             elements[getId(el)] = data;
22067             if(data.isHandle !== false){
22068                 handles[data.ddel.id] = data;
22069             }
22070             if(data.handles){
22071                 var hs = data.handles;
22072                 for(var i = 0, len = hs.length; i < len; i++){
22073                         handles[getId(hs[i])] = data;
22074                 }
22075             }
22076         },
22077
22078     /**
22079      * Unregister a drag drop element
22080      * @param {String|HTMLElement}  element The id or DOM node to unregister
22081      */
22082         unregister : function(el){
22083             var id = getId(el, false);
22084             var data = elements[id];
22085             if(data){
22086                 delete elements[id];
22087                 if(data.handles){
22088                     var hs = data.handles;
22089                     for(var i = 0, len = hs.length; i < len; i++){
22090                         delete handles[getId(hs[i], false)];
22091                     }
22092                 }
22093             }
22094         },
22095
22096     /**
22097      * Returns the handle registered for a DOM Node by id
22098      * @param {String|HTMLElement} id The DOM node or id to look up
22099      * @return {Object} handle The custom handle data
22100      */
22101         getHandle : function(id){
22102             if(typeof id != "string"){ // must be element?
22103                 id = id.id;
22104             }
22105             return handles[id];
22106         },
22107
22108     /**
22109      * Returns the handle that is registered for the DOM node that is the target of the event
22110      * @param {Event} e The event
22111      * @return {Object} handle The custom handle data
22112      */
22113         getHandleFromEvent : function(e){
22114             var t = Roo.lib.Event.getTarget(e);
22115             return t ? handles[t.id] : null;
22116         },
22117
22118     /**
22119      * Returns a custom data object that is registered for a DOM node by id
22120      * @param {String|HTMLElement} id The DOM node or id to look up
22121      * @return {Object} data The custom data
22122      */
22123         getTarget : function(id){
22124             if(typeof id != "string"){ // must be element?
22125                 id = id.id;
22126             }
22127             return elements[id];
22128         },
22129
22130     /**
22131      * Returns a custom data object that is registered for the DOM node that is the target of the event
22132      * @param {Event} e The event
22133      * @return {Object} data The custom data
22134      */
22135         getTargetFromEvent : function(e){
22136             var t = Roo.lib.Event.getTarget(e);
22137             return t ? elements[t.id] || handles[t.id] : null;
22138         }
22139     };
22140 }();/*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150  
22151
22152 /**
22153  * @class Roo.dd.StatusProxy
22154  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22155  * default drag proxy used by all Roo.dd components.
22156  * @constructor
22157  * @param {Object} config
22158  */
22159 Roo.dd.StatusProxy = function(config){
22160     Roo.apply(this, config);
22161     this.id = this.id || Roo.id();
22162     this.el = new Roo.Layer({
22163         dh: {
22164             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22165                 {tag: "div", cls: "x-dd-drop-icon"},
22166                 {tag: "div", cls: "x-dd-drag-ghost"}
22167             ]
22168         }, 
22169         shadow: !config || config.shadow !== false
22170     });
22171     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22172     this.dropStatus = this.dropNotAllowed;
22173 };
22174
22175 Roo.dd.StatusProxy.prototype = {
22176     /**
22177      * @cfg {String} dropAllowed
22178      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22179      */
22180     dropAllowed : "x-dd-drop-ok",
22181     /**
22182      * @cfg {String} dropNotAllowed
22183      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22184      */
22185     dropNotAllowed : "x-dd-drop-nodrop",
22186
22187     /**
22188      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22189      * over the current target element.
22190      * @param {String} cssClass The css class for the new drop status indicator image
22191      */
22192     setStatus : function(cssClass){
22193         cssClass = cssClass || this.dropNotAllowed;
22194         if(this.dropStatus != cssClass){
22195             this.el.replaceClass(this.dropStatus, cssClass);
22196             this.dropStatus = cssClass;
22197         }
22198     },
22199
22200     /**
22201      * Resets the status indicator to the default dropNotAllowed value
22202      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22203      */
22204     reset : function(clearGhost){
22205         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22206         this.dropStatus = this.dropNotAllowed;
22207         if(clearGhost){
22208             this.ghost.update("");
22209         }
22210     },
22211
22212     /**
22213      * Updates the contents of the ghost element
22214      * @param {String} html The html that will replace the current innerHTML of the ghost element
22215      */
22216     update : function(html){
22217         if(typeof html == "string"){
22218             this.ghost.update(html);
22219         }else{
22220             this.ghost.update("");
22221             html.style.margin = "0";
22222             this.ghost.dom.appendChild(html);
22223         }
22224         // ensure float = none set?? cant remember why though.
22225         var el = this.ghost.dom.firstChild;
22226                 if(el){
22227                         Roo.fly(el).setStyle('float', 'none');
22228                 }
22229     },
22230     
22231     /**
22232      * Returns the underlying proxy {@link Roo.Layer}
22233      * @return {Roo.Layer} el
22234     */
22235     getEl : function(){
22236         return this.el;
22237     },
22238
22239     /**
22240      * Returns the ghost element
22241      * @return {Roo.Element} el
22242      */
22243     getGhost : function(){
22244         return this.ghost;
22245     },
22246
22247     /**
22248      * Hides the proxy
22249      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22250      */
22251     hide : function(clear){
22252         this.el.hide();
22253         if(clear){
22254             this.reset(true);
22255         }
22256     },
22257
22258     /**
22259      * Stops the repair animation if it's currently running
22260      */
22261     stop : function(){
22262         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22263             this.anim.stop();
22264         }
22265     },
22266
22267     /**
22268      * Displays this proxy
22269      */
22270     show : function(){
22271         this.el.show();
22272     },
22273
22274     /**
22275      * Force the Layer to sync its shadow and shim positions to the element
22276      */
22277     sync : function(){
22278         this.el.sync();
22279     },
22280
22281     /**
22282      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22283      * invalid drop operation by the item being dragged.
22284      * @param {Array} xy The XY position of the element ([x, y])
22285      * @param {Function} callback The function to call after the repair is complete
22286      * @param {Object} scope The scope in which to execute the callback
22287      */
22288     repair : function(xy, callback, scope){
22289         this.callback = callback;
22290         this.scope = scope;
22291         if(xy && this.animRepair !== false){
22292             this.el.addClass("x-dd-drag-repair");
22293             this.el.hideUnders(true);
22294             this.anim = this.el.shift({
22295                 duration: this.repairDuration || .5,
22296                 easing: 'easeOut',
22297                 xy: xy,
22298                 stopFx: true,
22299                 callback: this.afterRepair,
22300                 scope: this
22301             });
22302         }else{
22303             this.afterRepair();
22304         }
22305     },
22306
22307     // private
22308     afterRepair : function(){
22309         this.hide(true);
22310         if(typeof this.callback == "function"){
22311             this.callback.call(this.scope || this);
22312         }
22313         this.callback = null;
22314         this.scope = null;
22315     }
22316 };/*
22317  * Based on:
22318  * Ext JS Library 1.1.1
22319  * Copyright(c) 2006-2007, Ext JS, LLC.
22320  *
22321  * Originally Released Under LGPL - original licence link has changed is not relivant.
22322  *
22323  * Fork - LGPL
22324  * <script type="text/javascript">
22325  */
22326
22327 /**
22328  * @class Roo.dd.DragSource
22329  * @extends Roo.dd.DDProxy
22330  * A simple class that provides the basic implementation needed to make any element draggable.
22331  * @constructor
22332  * @param {String/HTMLElement/Element} el The container element
22333  * @param {Object} config
22334  */
22335 Roo.dd.DragSource = function(el, config){
22336     this.el = Roo.get(el);
22337     this.dragData = {};
22338     
22339     Roo.apply(this, config);
22340     
22341     if(!this.proxy){
22342         this.proxy = new Roo.dd.StatusProxy();
22343     }
22344
22345     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22346           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22347     
22348     this.dragging = false;
22349 };
22350
22351 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22352     /**
22353      * @cfg {String} dropAllowed
22354      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22355      */
22356     dropAllowed : "x-dd-drop-ok",
22357     /**
22358      * @cfg {String} dropNotAllowed
22359      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22360      */
22361     dropNotAllowed : "x-dd-drop-nodrop",
22362
22363     /**
22364      * Returns the data object associated with this drag source
22365      * @return {Object} data An object containing arbitrary data
22366      */
22367     getDragData : function(e){
22368         return this.dragData;
22369     },
22370
22371     // private
22372     onDragEnter : function(e, id){
22373         var target = Roo.dd.DragDropMgr.getDDById(id);
22374         this.cachedTarget = target;
22375         if(this.beforeDragEnter(target, e, id) !== false){
22376             if(target.isNotifyTarget){
22377                 var status = target.notifyEnter(this, e, this.dragData);
22378                 this.proxy.setStatus(status);
22379             }else{
22380                 this.proxy.setStatus(this.dropAllowed);
22381             }
22382             
22383             if(this.afterDragEnter){
22384                 /**
22385                  * An empty function by default, but provided so that you can perform a custom action
22386                  * when the dragged item enters the drop target by providing an implementation.
22387                  * @param {Roo.dd.DragDrop} target The drop target
22388                  * @param {Event} e The event object
22389                  * @param {String} id The id of the dragged element
22390                  * @method afterDragEnter
22391                  */
22392                 this.afterDragEnter(target, e, id);
22393             }
22394         }
22395     },
22396
22397     /**
22398      * An empty function by default, but provided so that you can perform a custom action
22399      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22400      * @param {Roo.dd.DragDrop} target The drop target
22401      * @param {Event} e The event object
22402      * @param {String} id The id of the dragged element
22403      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22404      */
22405     beforeDragEnter : function(target, e, id){
22406         return true;
22407     },
22408
22409     // private
22410     alignElWithMouse: function() {
22411         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22412         this.proxy.sync();
22413     },
22414
22415     // private
22416     onDragOver : function(e, id){
22417         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22418         if(this.beforeDragOver(target, e, id) !== false){
22419             if(target.isNotifyTarget){
22420                 var status = target.notifyOver(this, e, this.dragData);
22421                 this.proxy.setStatus(status);
22422             }
22423
22424             if(this.afterDragOver){
22425                 /**
22426                  * An empty function by default, but provided so that you can perform a custom action
22427                  * while the dragged item is over the drop target by providing an implementation.
22428                  * @param {Roo.dd.DragDrop} target The drop target
22429                  * @param {Event} e The event object
22430                  * @param {String} id The id of the dragged element
22431                  * @method afterDragOver
22432                  */
22433                 this.afterDragOver(target, e, id);
22434             }
22435         }
22436     },
22437
22438     /**
22439      * An empty function by default, but provided so that you can perform a custom action
22440      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22441      * @param {Roo.dd.DragDrop} target The drop target
22442      * @param {Event} e The event object
22443      * @param {String} id The id of the dragged element
22444      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22445      */
22446     beforeDragOver : function(target, e, id){
22447         return true;
22448     },
22449
22450     // private
22451     onDragOut : function(e, id){
22452         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22453         if(this.beforeDragOut(target, e, id) !== false){
22454             if(target.isNotifyTarget){
22455                 target.notifyOut(this, e, this.dragData);
22456             }
22457             this.proxy.reset();
22458             if(this.afterDragOut){
22459                 /**
22460                  * An empty function by default, but provided so that you can perform a custom action
22461                  * after the dragged item is dragged out of the target without dropping.
22462                  * @param {Roo.dd.DragDrop} target The drop target
22463                  * @param {Event} e The event object
22464                  * @param {String} id The id of the dragged element
22465                  * @method afterDragOut
22466                  */
22467                 this.afterDragOut(target, e, id);
22468             }
22469         }
22470         this.cachedTarget = null;
22471     },
22472
22473     /**
22474      * An empty function by default, but provided so that you can perform a custom action before the dragged
22475      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22476      * @param {Roo.dd.DragDrop} target The drop target
22477      * @param {Event} e The event object
22478      * @param {String} id The id of the dragged element
22479      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22480      */
22481     beforeDragOut : function(target, e, id){
22482         return true;
22483     },
22484     
22485     // private
22486     onDragDrop : function(e, id){
22487         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22488         if(this.beforeDragDrop(target, e, id) !== false){
22489             if(target.isNotifyTarget){
22490                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22491                     this.onValidDrop(target, e, id);
22492                 }else{
22493                     this.onInvalidDrop(target, e, id);
22494                 }
22495             }else{
22496                 this.onValidDrop(target, e, id);
22497             }
22498             
22499             if(this.afterDragDrop){
22500                 /**
22501                  * An empty function by default, but provided so that you can perform a custom action
22502                  * after a valid drag drop has occurred by providing an implementation.
22503                  * @param {Roo.dd.DragDrop} target The drop target
22504                  * @param {Event} e The event object
22505                  * @param {String} id The id of the dropped element
22506                  * @method afterDragDrop
22507                  */
22508                 this.afterDragDrop(target, e, id);
22509             }
22510         }
22511         delete this.cachedTarget;
22512     },
22513
22514     /**
22515      * An empty function by default, but provided so that you can perform a custom action before the dragged
22516      * item is dropped onto the target and optionally cancel the onDragDrop.
22517      * @param {Roo.dd.DragDrop} target The drop target
22518      * @param {Event} e The event object
22519      * @param {String} id The id of the dragged element
22520      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22521      */
22522     beforeDragDrop : function(target, e, id){
22523         return true;
22524     },
22525
22526     // private
22527     onValidDrop : function(target, e, id){
22528         this.hideProxy();
22529         if(this.afterValidDrop){
22530             /**
22531              * An empty function by default, but provided so that you can perform a custom action
22532              * after a valid drop has occurred by providing an implementation.
22533              * @param {Object} target The target DD 
22534              * @param {Event} e The event object
22535              * @param {String} id The id of the dropped element
22536              * @method afterInvalidDrop
22537              */
22538             this.afterValidDrop(target, e, id);
22539         }
22540     },
22541
22542     // private
22543     getRepairXY : function(e, data){
22544         return this.el.getXY();  
22545     },
22546
22547     // private
22548     onInvalidDrop : function(target, e, id){
22549         this.beforeInvalidDrop(target, e, id);
22550         if(this.cachedTarget){
22551             if(this.cachedTarget.isNotifyTarget){
22552                 this.cachedTarget.notifyOut(this, e, this.dragData);
22553             }
22554             this.cacheTarget = null;
22555         }
22556         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22557
22558         if(this.afterInvalidDrop){
22559             /**
22560              * An empty function by default, but provided so that you can perform a custom action
22561              * after an invalid drop has occurred by providing an implementation.
22562              * @param {Event} e The event object
22563              * @param {String} id The id of the dropped element
22564              * @method afterInvalidDrop
22565              */
22566             this.afterInvalidDrop(e, id);
22567         }
22568     },
22569
22570     // private
22571     afterRepair : function(){
22572         if(Roo.enableFx){
22573             this.el.highlight(this.hlColor || "c3daf9");
22574         }
22575         this.dragging = false;
22576     },
22577
22578     /**
22579      * An empty function by default, but provided so that you can perform a custom action after an invalid
22580      * drop has occurred.
22581      * @param {Roo.dd.DragDrop} target The drop target
22582      * @param {Event} e The event object
22583      * @param {String} id The id of the dragged element
22584      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22585      */
22586     beforeInvalidDrop : function(target, e, id){
22587         return true;
22588     },
22589
22590     // private
22591     handleMouseDown : function(e){
22592         if(this.dragging) {
22593             return;
22594         }
22595         var data = this.getDragData(e);
22596         if(data && this.onBeforeDrag(data, e) !== false){
22597             this.dragData = data;
22598             this.proxy.stop();
22599             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22600         } 
22601     },
22602
22603     /**
22604      * An empty function by default, but provided so that you can perform a custom action before the initial
22605      * drag event begins and optionally cancel it.
22606      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22607      * @param {Event} e The event object
22608      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22609      */
22610     onBeforeDrag : function(data, e){
22611         return true;
22612     },
22613
22614     /**
22615      * An empty function by default, but provided so that you can perform a custom action once the initial
22616      * drag event has begun.  The drag cannot be canceled from this function.
22617      * @param {Number} x The x position of the click on the dragged object
22618      * @param {Number} y The y position of the click on the dragged object
22619      */
22620     onStartDrag : Roo.emptyFn,
22621
22622     // private - YUI override
22623     startDrag : function(x, y){
22624         this.proxy.reset();
22625         this.dragging = true;
22626         this.proxy.update("");
22627         this.onInitDrag(x, y);
22628         this.proxy.show();
22629     },
22630
22631     // private
22632     onInitDrag : function(x, y){
22633         var clone = this.el.dom.cloneNode(true);
22634         clone.id = Roo.id(); // prevent duplicate ids
22635         this.proxy.update(clone);
22636         this.onStartDrag(x, y);
22637         return true;
22638     },
22639
22640     /**
22641      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22642      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22643      */
22644     getProxy : function(){
22645         return this.proxy;  
22646     },
22647
22648     /**
22649      * Hides the drag source's {@link Roo.dd.StatusProxy}
22650      */
22651     hideProxy : function(){
22652         this.proxy.hide();  
22653         this.proxy.reset(true);
22654         this.dragging = false;
22655     },
22656
22657     // private
22658     triggerCacheRefresh : function(){
22659         Roo.dd.DDM.refreshCache(this.groups);
22660     },
22661
22662     // private - override to prevent hiding
22663     b4EndDrag: function(e) {
22664     },
22665
22666     // private - override to prevent moving
22667     endDrag : function(e){
22668         this.onEndDrag(this.dragData, e);
22669     },
22670
22671     // private
22672     onEndDrag : function(data, e){
22673     },
22674     
22675     // private - pin to cursor
22676     autoOffset : function(x, y) {
22677         this.setDelta(-12, -20);
22678     }    
22679 });/*
22680  * Based on:
22681  * Ext JS Library 1.1.1
22682  * Copyright(c) 2006-2007, Ext JS, LLC.
22683  *
22684  * Originally Released Under LGPL - original licence link has changed is not relivant.
22685  *
22686  * Fork - LGPL
22687  * <script type="text/javascript">
22688  */
22689
22690
22691 /**
22692  * @class Roo.dd.DropTarget
22693  * @extends Roo.dd.DDTarget
22694  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22695  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22696  * @constructor
22697  * @param {String/HTMLElement/Element} el The container element
22698  * @param {Object} config
22699  */
22700 Roo.dd.DropTarget = function(el, config){
22701     this.el = Roo.get(el);
22702     
22703     var listeners = false; ;
22704     if (config && config.listeners) {
22705         listeners= config.listeners;
22706         delete config.listeners;
22707     }
22708     Roo.apply(this, config);
22709     
22710     if(this.containerScroll){
22711         Roo.dd.ScrollManager.register(this.el);
22712     }
22713     this.addEvents( {
22714          /**
22715          * @scope Roo.dd.DropTarget
22716          */
22717          
22718          /**
22719          * @event enter
22720          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22721          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22722          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22723          * 
22724          * IMPORTANT : it should set  this.valid to true|false
22725          * 
22726          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22727          * @param {Event} e The event
22728          * @param {Object} data An object containing arbitrary data supplied by the drag source
22729          */
22730         "enter" : true,
22731         
22732          /**
22733          * @event over
22734          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22735          * This method will be called on every mouse movement while the drag source is over the drop target.
22736          * This default implementation simply returns the dropAllowed config value.
22737          * 
22738          * IMPORTANT : it should set  this.valid to true|false
22739          * 
22740          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22741          * @param {Event} e The event
22742          * @param {Object} data An object containing arbitrary data supplied by the drag source
22743          
22744          */
22745         "over" : true,
22746         /**
22747          * @event out
22748          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22749          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22750          * overClass (if any) from the drop element.
22751          * 
22752          * 
22753          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22754          * @param {Event} e The event
22755          * @param {Object} data An object containing arbitrary data supplied by the drag source
22756          */
22757          "out" : true,
22758          
22759         /**
22760          * @event drop
22761          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22762          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22763          * implementation that does something to process the drop event and returns true so that the drag source's
22764          * repair action does not run.
22765          * 
22766          * IMPORTANT : it should set this.success
22767          * 
22768          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22769          * @param {Event} e The event
22770          * @param {Object} data An object containing arbitrary data supplied by the drag source
22771         */
22772          "drop" : true
22773     });
22774             
22775      
22776     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22777         this.el.dom, 
22778         this.ddGroup || this.group,
22779         {
22780             isTarget: true,
22781             listeners : listeners || {} 
22782            
22783         
22784         }
22785     );
22786
22787 };
22788
22789 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22790     /**
22791      * @cfg {String} overClass
22792      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22793      */
22794      /**
22795      * @cfg {String} ddGroup
22796      * The drag drop group to handle drop events for
22797      */
22798      
22799     /**
22800      * @cfg {String} dropAllowed
22801      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22802      */
22803     dropAllowed : "x-dd-drop-ok",
22804     /**
22805      * @cfg {String} dropNotAllowed
22806      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22807      */
22808     dropNotAllowed : "x-dd-drop-nodrop",
22809     /**
22810      * @cfg {boolean} success
22811      * set this after drop listener.. 
22812      */
22813     success : false,
22814     /**
22815      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22816      * if the drop point is valid for over/enter..
22817      */
22818     valid : false,
22819     // private
22820     isTarget : true,
22821
22822     // private
22823     isNotifyTarget : true,
22824     
22825     /**
22826      * @hide
22827      */
22828     notifyEnter : function(dd, e, data)
22829     {
22830         this.valid = true;
22831         this.fireEvent('enter', dd, e, data);
22832         if(this.overClass){
22833             this.el.addClass(this.overClass);
22834         }
22835         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22836             this.valid ? this.dropAllowed : this.dropNotAllowed
22837         );
22838     },
22839
22840     /**
22841      * @hide
22842      */
22843     notifyOver : function(dd, e, data)
22844     {
22845         this.valid = true;
22846         this.fireEvent('over', dd, e, data);
22847         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22848             this.valid ? this.dropAllowed : this.dropNotAllowed
22849         );
22850     },
22851
22852     /**
22853      * @hide
22854      */
22855     notifyOut : function(dd, e, data)
22856     {
22857         this.fireEvent('out', dd, e, data);
22858         if(this.overClass){
22859             this.el.removeClass(this.overClass);
22860         }
22861     },
22862
22863     /**
22864      * @hide
22865      */
22866     notifyDrop : function(dd, e, data)
22867     {
22868         this.success = false;
22869         this.fireEvent('drop', dd, e, data);
22870         return this.success;
22871     }
22872 });/*
22873  * Based on:
22874  * Ext JS Library 1.1.1
22875  * Copyright(c) 2006-2007, Ext JS, LLC.
22876  *
22877  * Originally Released Under LGPL - original licence link has changed is not relivant.
22878  *
22879  * Fork - LGPL
22880  * <script type="text/javascript">
22881  */
22882
22883
22884 /**
22885  * @class Roo.dd.DragZone
22886  * @extends Roo.dd.DragSource
22887  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22888  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22889  * @constructor
22890  * @param {String/HTMLElement/Element} el The container element
22891  * @param {Object} config
22892  */
22893 Roo.dd.DragZone = function(el, config){
22894     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22895     if(this.containerScroll){
22896         Roo.dd.ScrollManager.register(this.el);
22897     }
22898 };
22899
22900 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22901     /**
22902      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22903      * for auto scrolling during drag operations.
22904      */
22905     /**
22906      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22907      * method after a failed drop (defaults to "c3daf9" - light blue)
22908      */
22909
22910     /**
22911      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22912      * for a valid target to drag based on the mouse down. Override this method
22913      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22914      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22915      * @param {EventObject} e The mouse down event
22916      * @return {Object} The dragData
22917      */
22918     getDragData : function(e){
22919         return Roo.dd.Registry.getHandleFromEvent(e);
22920     },
22921     
22922     /**
22923      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22924      * this.dragData.ddel
22925      * @param {Number} x The x position of the click on the dragged object
22926      * @param {Number} y The y position of the click on the dragged object
22927      * @return {Boolean} true to continue the drag, false to cancel
22928      */
22929     onInitDrag : function(x, y){
22930         this.proxy.update(this.dragData.ddel.cloneNode(true));
22931         this.onStartDrag(x, y);
22932         return true;
22933     },
22934     
22935     /**
22936      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22937      */
22938     afterRepair : function(){
22939         if(Roo.enableFx){
22940             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22941         }
22942         this.dragging = false;
22943     },
22944
22945     /**
22946      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22947      * the XY of this.dragData.ddel
22948      * @param {EventObject} e The mouse up event
22949      * @return {Array} The xy location (e.g. [100, 200])
22950      */
22951     getRepairXY : function(e){
22952         return Roo.Element.fly(this.dragData.ddel).getXY();  
22953     }
22954 });/*
22955  * Based on:
22956  * Ext JS Library 1.1.1
22957  * Copyright(c) 2006-2007, Ext JS, LLC.
22958  *
22959  * Originally Released Under LGPL - original licence link has changed is not relivant.
22960  *
22961  * Fork - LGPL
22962  * <script type="text/javascript">
22963  */
22964 /**
22965  * @class Roo.dd.DropZone
22966  * @extends Roo.dd.DropTarget
22967  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22968  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22969  * @constructor
22970  * @param {String/HTMLElement/Element} el The container element
22971  * @param {Object} config
22972  */
22973 Roo.dd.DropZone = function(el, config){
22974     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22975 };
22976
22977 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22978     /**
22979      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22980      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22981      * provide your own custom lookup.
22982      * @param {Event} e The event
22983      * @return {Object} data The custom data
22984      */
22985     getTargetFromEvent : function(e){
22986         return Roo.dd.Registry.getTargetFromEvent(e);
22987     },
22988
22989     /**
22990      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22991      * that it has registered.  This method has no default implementation and should be overridden to provide
22992      * node-specific processing if necessary.
22993      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22994      * {@link #getTargetFromEvent} for this node)
22995      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22996      * @param {Event} e The event
22997      * @param {Object} data An object containing arbitrary data supplied by the drag source
22998      */
22999     onNodeEnter : function(n, dd, e, data){
23000         
23001     },
23002
23003     /**
23004      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23005      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23006      * overridden to provide the proper feedback.
23007      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23008      * {@link #getTargetFromEvent} for this node)
23009      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23010      * @param {Event} e The event
23011      * @param {Object} data An object containing arbitrary data supplied by the drag source
23012      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23013      * underlying {@link Roo.dd.StatusProxy} can be updated
23014      */
23015     onNodeOver : function(n, dd, e, data){
23016         return this.dropAllowed;
23017     },
23018
23019     /**
23020      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23021      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23022      * node-specific processing if necessary.
23023      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23024      * {@link #getTargetFromEvent} for this node)
23025      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23026      * @param {Event} e The event
23027      * @param {Object} data An object containing arbitrary data supplied by the drag source
23028      */
23029     onNodeOut : function(n, dd, e, data){
23030         
23031     },
23032
23033     /**
23034      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23035      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23036      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23037      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23038      * {@link #getTargetFromEvent} for this node)
23039      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23040      * @param {Event} e The event
23041      * @param {Object} data An object containing arbitrary data supplied by the drag source
23042      * @return {Boolean} True if the drop was valid, else false
23043      */
23044     onNodeDrop : function(n, dd, e, data){
23045         return false;
23046     },
23047
23048     /**
23049      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23050      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23051      * it should be overridden to provide the proper feedback if necessary.
23052      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23053      * @param {Event} e The event
23054      * @param {Object} data An object containing arbitrary data supplied by the drag source
23055      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23056      * underlying {@link Roo.dd.StatusProxy} can be updated
23057      */
23058     onContainerOver : function(dd, e, data){
23059         return this.dropNotAllowed;
23060     },
23061
23062     /**
23063      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23064      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23065      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23066      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23067      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23068      * @param {Event} e The event
23069      * @param {Object} data An object containing arbitrary data supplied by the drag source
23070      * @return {Boolean} True if the drop was valid, else false
23071      */
23072     onContainerDrop : function(dd, e, data){
23073         return false;
23074     },
23075
23076     /**
23077      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23078      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23079      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23080      * you should override this method and provide a custom implementation.
23081      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23082      * @param {Event} e The event
23083      * @param {Object} data An object containing arbitrary data supplied by the drag source
23084      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23085      * underlying {@link Roo.dd.StatusProxy} can be updated
23086      */
23087     notifyEnter : function(dd, e, data){
23088         return this.dropNotAllowed;
23089     },
23090
23091     /**
23092      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23093      * This method will be called on every mouse movement while the drag source is over the drop zone.
23094      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23095      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23096      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23097      * registered node, it will call {@link #onContainerOver}.
23098      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23099      * @param {Event} e The event
23100      * @param {Object} data An object containing arbitrary data supplied by the drag source
23101      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23102      * underlying {@link Roo.dd.StatusProxy} can be updated
23103      */
23104     notifyOver : function(dd, e, data){
23105         var n = this.getTargetFromEvent(e);
23106         if(!n){ // not over valid drop target
23107             if(this.lastOverNode){
23108                 this.onNodeOut(this.lastOverNode, dd, e, data);
23109                 this.lastOverNode = null;
23110             }
23111             return this.onContainerOver(dd, e, data);
23112         }
23113         if(this.lastOverNode != n){
23114             if(this.lastOverNode){
23115                 this.onNodeOut(this.lastOverNode, dd, e, data);
23116             }
23117             this.onNodeEnter(n, dd, e, data);
23118             this.lastOverNode = n;
23119         }
23120         return this.onNodeOver(n, dd, e, data);
23121     },
23122
23123     /**
23124      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23125      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23126      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23127      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23128      * @param {Event} e The event
23129      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23130      */
23131     notifyOut : function(dd, e, data){
23132         if(this.lastOverNode){
23133             this.onNodeOut(this.lastOverNode, dd, e, data);
23134             this.lastOverNode = null;
23135         }
23136     },
23137
23138     /**
23139      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23140      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23141      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23142      * otherwise it will call {@link #onContainerDrop}.
23143      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23144      * @param {Event} e The event
23145      * @param {Object} data An object containing arbitrary data supplied by the drag source
23146      * @return {Boolean} True if the drop was valid, else false
23147      */
23148     notifyDrop : function(dd, e, data){
23149         if(this.lastOverNode){
23150             this.onNodeOut(this.lastOverNode, dd, e, data);
23151             this.lastOverNode = null;
23152         }
23153         var n = this.getTargetFromEvent(e);
23154         return n ?
23155             this.onNodeDrop(n, dd, e, data) :
23156             this.onContainerDrop(dd, e, data);
23157     },
23158
23159     // private
23160     triggerCacheRefresh : function(){
23161         Roo.dd.DDM.refreshCache(this.groups);
23162     }  
23163 });/*
23164  * Based on:
23165  * Ext JS Library 1.1.1
23166  * Copyright(c) 2006-2007, Ext JS, LLC.
23167  *
23168  * Originally Released Under LGPL - original licence link has changed is not relivant.
23169  *
23170  * Fork - LGPL
23171  * <script type="text/javascript">
23172  */
23173
23174
23175 /**
23176  * @class Roo.data.SortTypes
23177  * @singleton
23178  * Defines the default sorting (casting?) comparison functions used when sorting data.
23179  */
23180 Roo.data.SortTypes = {
23181     /**
23182      * Default sort that does nothing
23183      * @param {Mixed} s The value being converted
23184      * @return {Mixed} The comparison value
23185      */
23186     none : function(s){
23187         return s;
23188     },
23189     
23190     /**
23191      * The regular expression used to strip tags
23192      * @type {RegExp}
23193      * @property
23194      */
23195     stripTagsRE : /<\/?[^>]+>/gi,
23196     
23197     /**
23198      * Strips all HTML tags to sort on text only
23199      * @param {Mixed} s The value being converted
23200      * @return {String} The comparison value
23201      */
23202     asText : function(s){
23203         return String(s).replace(this.stripTagsRE, "");
23204     },
23205     
23206     /**
23207      * Strips all HTML tags to sort on text only - Case insensitive
23208      * @param {Mixed} s The value being converted
23209      * @return {String} The comparison value
23210      */
23211     asUCText : function(s){
23212         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23213     },
23214     
23215     /**
23216      * Case insensitive string
23217      * @param {Mixed} s The value being converted
23218      * @return {String} The comparison value
23219      */
23220     asUCString : function(s) {
23221         return String(s).toUpperCase();
23222     },
23223     
23224     /**
23225      * Date sorting
23226      * @param {Mixed} s The value being converted
23227      * @return {Number} The comparison value
23228      */
23229     asDate : function(s) {
23230         if(!s){
23231             return 0;
23232         }
23233         if(s instanceof Date){
23234             return s.getTime();
23235         }
23236         return Date.parse(String(s));
23237     },
23238     
23239     /**
23240      * Float sorting
23241      * @param {Mixed} s The value being converted
23242      * @return {Float} The comparison value
23243      */
23244     asFloat : function(s) {
23245         var val = parseFloat(String(s).replace(/,/g, ""));
23246         if(isNaN(val)) {
23247             val = 0;
23248         }
23249         return val;
23250     },
23251     
23252     /**
23253      * Integer sorting
23254      * @param {Mixed} s The value being converted
23255      * @return {Number} The comparison value
23256      */
23257     asInt : function(s) {
23258         var val = parseInt(String(s).replace(/,/g, ""));
23259         if(isNaN(val)) {
23260             val = 0;
23261         }
23262         return val;
23263     }
23264 };/*
23265  * Based on:
23266  * Ext JS Library 1.1.1
23267  * Copyright(c) 2006-2007, Ext JS, LLC.
23268  *
23269  * Originally Released Under LGPL - original licence link has changed is not relivant.
23270  *
23271  * Fork - LGPL
23272  * <script type="text/javascript">
23273  */
23274
23275 /**
23276 * @class Roo.data.Record
23277  * Instances of this class encapsulate both record <em>definition</em> information, and record
23278  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23279  * to access Records cached in an {@link Roo.data.Store} object.<br>
23280  * <p>
23281  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23282  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23283  * objects.<br>
23284  * <p>
23285  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23286  * @constructor
23287  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23288  * {@link #create}. The parameters are the same.
23289  * @param {Array} data An associative Array of data values keyed by the field name.
23290  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23291  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23292  * not specified an integer id is generated.
23293  */
23294 Roo.data.Record = function(data, id){
23295     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23296     this.data = data;
23297 };
23298
23299 /**
23300  * Generate a constructor for a specific record layout.
23301  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23302  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23303  * Each field definition object may contain the following properties: <ul>
23304  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
23305  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23306  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23307  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23308  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23309  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23310  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23311  * this may be omitted.</p></li>
23312  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23313  * <ul><li>auto (Default, implies no conversion)</li>
23314  * <li>string</li>
23315  * <li>int</li>
23316  * <li>float</li>
23317  * <li>boolean</li>
23318  * <li>date</li></ul></p></li>
23319  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23320  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23321  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23322  * by the Reader into an object that will be stored in the Record. It is passed the
23323  * following parameters:<ul>
23324  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23325  * </ul></p></li>
23326  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23327  * </ul>
23328  * <br>usage:<br><pre><code>
23329 var TopicRecord = Roo.data.Record.create(
23330     {name: 'title', mapping: 'topic_title'},
23331     {name: 'author', mapping: 'username'},
23332     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23333     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23334     {name: 'lastPoster', mapping: 'user2'},
23335     {name: 'excerpt', mapping: 'post_text'}
23336 );
23337
23338 var myNewRecord = new TopicRecord({
23339     title: 'Do my job please',
23340     author: 'noobie',
23341     totalPosts: 1,
23342     lastPost: new Date(),
23343     lastPoster: 'Animal',
23344     excerpt: 'No way dude!'
23345 });
23346 myStore.add(myNewRecord);
23347 </code></pre>
23348  * @method create
23349  * @static
23350  */
23351 Roo.data.Record.create = function(o){
23352     var f = function(){
23353         f.superclass.constructor.apply(this, arguments);
23354     };
23355     Roo.extend(f, Roo.data.Record);
23356     var p = f.prototype;
23357     p.fields = new Roo.util.MixedCollection(false, function(field){
23358         return field.name;
23359     });
23360     for(var i = 0, len = o.length; i < len; i++){
23361         p.fields.add(new Roo.data.Field(o[i]));
23362     }
23363     f.getField = function(name){
23364         return p.fields.get(name);  
23365     };
23366     return f;
23367 };
23368
23369 Roo.data.Record.AUTO_ID = 1000;
23370 Roo.data.Record.EDIT = 'edit';
23371 Roo.data.Record.REJECT = 'reject';
23372 Roo.data.Record.COMMIT = 'commit';
23373
23374 Roo.data.Record.prototype = {
23375     /**
23376      * Readonly flag - true if this record has been modified.
23377      * @type Boolean
23378      */
23379     dirty : false,
23380     editing : false,
23381     error: null,
23382     modified: null,
23383
23384     // private
23385     join : function(store){
23386         this.store = store;
23387     },
23388
23389     /**
23390      * Set the named field to the specified value.
23391      * @param {String} name The name of the field to set.
23392      * @param {Object} value The value to set the field to.
23393      */
23394     set : function(name, value){
23395         if(this.data[name] == value){
23396             return;
23397         }
23398         this.dirty = true;
23399         if(!this.modified){
23400             this.modified = {};
23401         }
23402         if(typeof this.modified[name] == 'undefined'){
23403             this.modified[name] = this.data[name];
23404         }
23405         this.data[name] = value;
23406         if(!this.editing && this.store){
23407             this.store.afterEdit(this);
23408         }       
23409     },
23410
23411     /**
23412      * Get the value of the named field.
23413      * @param {String} name The name of the field to get the value of.
23414      * @return {Object} The value of the field.
23415      */
23416     get : function(name){
23417         return this.data[name]; 
23418     },
23419
23420     // private
23421     beginEdit : function(){
23422         this.editing = true;
23423         this.modified = {}; 
23424     },
23425
23426     // private
23427     cancelEdit : function(){
23428         this.editing = false;
23429         delete this.modified;
23430     },
23431
23432     // private
23433     endEdit : function(){
23434         this.editing = false;
23435         if(this.dirty && this.store){
23436             this.store.afterEdit(this);
23437         }
23438     },
23439
23440     /**
23441      * Usually called by the {@link Roo.data.Store} which owns the Record.
23442      * Rejects all changes made to the Record since either creation, or the last commit operation.
23443      * Modified fields are reverted to their original values.
23444      * <p>
23445      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23446      * of reject operations.
23447      */
23448     reject : function(){
23449         var m = this.modified;
23450         for(var n in m){
23451             if(typeof m[n] != "function"){
23452                 this.data[n] = m[n];
23453             }
23454         }
23455         this.dirty = false;
23456         delete this.modified;
23457         this.editing = false;
23458         if(this.store){
23459             this.store.afterReject(this);
23460         }
23461     },
23462
23463     /**
23464      * Usually called by the {@link Roo.data.Store} which owns the Record.
23465      * Commits all changes made to the Record since either creation, or the last commit operation.
23466      * <p>
23467      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23468      * of commit operations.
23469      */
23470     commit : function(){
23471         this.dirty = false;
23472         delete this.modified;
23473         this.editing = false;
23474         if(this.store){
23475             this.store.afterCommit(this);
23476         }
23477     },
23478
23479     // private
23480     hasError : function(){
23481         return this.error != null;
23482     },
23483
23484     // private
23485     clearError : function(){
23486         this.error = null;
23487     },
23488
23489     /**
23490      * Creates a copy of this record.
23491      * @param {String} id (optional) A new record id if you don't want to use this record's id
23492      * @return {Record}
23493      */
23494     copy : function(newId) {
23495         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23496     }
23497 };/*
23498  * Based on:
23499  * Ext JS Library 1.1.1
23500  * Copyright(c) 2006-2007, Ext JS, LLC.
23501  *
23502  * Originally Released Under LGPL - original licence link has changed is not relivant.
23503  *
23504  * Fork - LGPL
23505  * <script type="text/javascript">
23506  */
23507
23508
23509
23510 /**
23511  * @class Roo.data.Store
23512  * @extends Roo.util.Observable
23513  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23514  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23515  * <p>
23516  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
23517  * has no knowledge of the format of the data returned by the Proxy.<br>
23518  * <p>
23519  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23520  * instances from the data object. These records are cached and made available through accessor functions.
23521  * @constructor
23522  * Creates a new Store.
23523  * @param {Object} config A config object containing the objects needed for the Store to access data,
23524  * and read the data into Records.
23525  */
23526 Roo.data.Store = function(config){
23527     this.data = new Roo.util.MixedCollection(false);
23528     this.data.getKey = function(o){
23529         return o.id;
23530     };
23531     this.baseParams = {};
23532     // private
23533     this.paramNames = {
23534         "start" : "start",
23535         "limit" : "limit",
23536         "sort" : "sort",
23537         "dir" : "dir",
23538         "multisort" : "_multisort"
23539     };
23540
23541     if(config && config.data){
23542         this.inlineData = config.data;
23543         delete config.data;
23544     }
23545
23546     Roo.apply(this, config);
23547     
23548     if(this.reader){ // reader passed
23549         this.reader = Roo.factory(this.reader, Roo.data);
23550         this.reader.xmodule = this.xmodule || false;
23551         if(!this.recordType){
23552             this.recordType = this.reader.recordType;
23553         }
23554         if(this.reader.onMetaChange){
23555             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23556         }
23557     }
23558
23559     if(this.recordType){
23560         this.fields = this.recordType.prototype.fields;
23561     }
23562     this.modified = [];
23563
23564     this.addEvents({
23565         /**
23566          * @event datachanged
23567          * Fires when the data cache has changed, and a widget which is using this Store
23568          * as a Record cache should refresh its view.
23569          * @param {Store} this
23570          */
23571         datachanged : true,
23572         /**
23573          * @event metachange
23574          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23575          * @param {Store} this
23576          * @param {Object} meta The JSON metadata
23577          */
23578         metachange : true,
23579         /**
23580          * @event add
23581          * Fires when Records have been added to the Store
23582          * @param {Store} this
23583          * @param {Roo.data.Record[]} records The array of Records added
23584          * @param {Number} index The index at which the record(s) were added
23585          */
23586         add : true,
23587         /**
23588          * @event remove
23589          * Fires when a Record has been removed from the Store
23590          * @param {Store} this
23591          * @param {Roo.data.Record} record The Record that was removed
23592          * @param {Number} index The index at which the record was removed
23593          */
23594         remove : true,
23595         /**
23596          * @event update
23597          * Fires when a Record has been updated
23598          * @param {Store} this
23599          * @param {Roo.data.Record} record The Record that was updated
23600          * @param {String} operation The update operation being performed.  Value may be one of:
23601          * <pre><code>
23602  Roo.data.Record.EDIT
23603  Roo.data.Record.REJECT
23604  Roo.data.Record.COMMIT
23605          * </code></pre>
23606          */
23607         update : true,
23608         /**
23609          * @event clear
23610          * Fires when the data cache has been cleared.
23611          * @param {Store} this
23612          */
23613         clear : true,
23614         /**
23615          * @event beforeload
23616          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23617          * the load action will be canceled.
23618          * @param {Store} this
23619          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23620          */
23621         beforeload : true,
23622         /**
23623          * @event beforeloadadd
23624          * Fires after a new set of Records has been loaded.
23625          * @param {Store} this
23626          * @param {Roo.data.Record[]} records The Records that were loaded
23627          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23628          */
23629         beforeloadadd : true,
23630         /**
23631          * @event load
23632          * Fires after a new set of Records has been loaded, before they are added to the store.
23633          * @param {Store} this
23634          * @param {Roo.data.Record[]} records The Records that were loaded
23635          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23636          * @params {Object} return from reader
23637          */
23638         load : true,
23639         /**
23640          * @event loadexception
23641          * Fires if an exception occurs in the Proxy during loading.
23642          * Called with the signature of the Proxy's "loadexception" event.
23643          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23644          * 
23645          * @param {Proxy} 
23646          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23647          * @param {Object} load options 
23648          * @param {Object} jsonData from your request (normally this contains the Exception)
23649          */
23650         loadexception : true
23651     });
23652     
23653     if(this.proxy){
23654         this.proxy = Roo.factory(this.proxy, Roo.data);
23655         this.proxy.xmodule = this.xmodule || false;
23656         this.relayEvents(this.proxy,  ["loadexception"]);
23657     }
23658     this.sortToggle = {};
23659     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23660
23661     Roo.data.Store.superclass.constructor.call(this);
23662
23663     if(this.inlineData){
23664         this.loadData(this.inlineData);
23665         delete this.inlineData;
23666     }
23667 };
23668
23669 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23670      /**
23671     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23672     * without a remote query - used by combo/forms at present.
23673     */
23674     
23675     /**
23676     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
23677     */
23678     /**
23679     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23680     */
23681     /**
23682     * @cfg {Roo.data.Reader} reader [required]  The Reader object which processes the data object and returns
23683     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23684     */
23685     /**
23686     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23687     * on any HTTP request
23688     */
23689     /**
23690     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23691     */
23692     /**
23693     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23694     */
23695     multiSort: false,
23696     /**
23697     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23698     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23699     */
23700     remoteSort : false,
23701
23702     /**
23703     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23704      * loaded or when a record is removed. (defaults to false).
23705     */
23706     pruneModifiedRecords : false,
23707
23708     // private
23709     lastOptions : null,
23710
23711     /**
23712      * Add Records to the Store and fires the add event.
23713      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23714      */
23715     add : function(records){
23716         records = [].concat(records);
23717         for(var i = 0, len = records.length; i < len; i++){
23718             records[i].join(this);
23719         }
23720         var index = this.data.length;
23721         this.data.addAll(records);
23722         this.fireEvent("add", this, records, index);
23723     },
23724
23725     /**
23726      * Remove a Record from the Store and fires the remove event.
23727      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23728      */
23729     remove : function(record){
23730         var index = this.data.indexOf(record);
23731         this.data.removeAt(index);
23732  
23733         if(this.pruneModifiedRecords){
23734             this.modified.remove(record);
23735         }
23736         this.fireEvent("remove", this, record, index);
23737     },
23738
23739     /**
23740      * Remove all Records from the Store and fires the clear event.
23741      */
23742     removeAll : function(){
23743         this.data.clear();
23744         if(this.pruneModifiedRecords){
23745             this.modified = [];
23746         }
23747         this.fireEvent("clear", this);
23748     },
23749
23750     /**
23751      * Inserts Records to the Store at the given index and fires the add event.
23752      * @param {Number} index The start index at which to insert the passed Records.
23753      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23754      */
23755     insert : function(index, records){
23756         records = [].concat(records);
23757         for(var i = 0, len = records.length; i < len; i++){
23758             this.data.insert(index, records[i]);
23759             records[i].join(this);
23760         }
23761         this.fireEvent("add", this, records, index);
23762     },
23763
23764     /**
23765      * Get the index within the cache of the passed Record.
23766      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23767      * @return {Number} The index of the passed Record. Returns -1 if not found.
23768      */
23769     indexOf : function(record){
23770         return this.data.indexOf(record);
23771     },
23772
23773     /**
23774      * Get the index within the cache of the Record with the passed id.
23775      * @param {String} id The id of the Record to find.
23776      * @return {Number} The index of the Record. Returns -1 if not found.
23777      */
23778     indexOfId : function(id){
23779         return this.data.indexOfKey(id);
23780     },
23781
23782     /**
23783      * Get the Record with the specified id.
23784      * @param {String} id The id of the Record to find.
23785      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23786      */
23787     getById : function(id){
23788         return this.data.key(id);
23789     },
23790
23791     /**
23792      * Get the Record at the specified index.
23793      * @param {Number} index The index of the Record to find.
23794      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23795      */
23796     getAt : function(index){
23797         return this.data.itemAt(index);
23798     },
23799
23800     /**
23801      * Returns a range of Records between specified indices.
23802      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23803      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23804      * @return {Roo.data.Record[]} An array of Records
23805      */
23806     getRange : function(start, end){
23807         return this.data.getRange(start, end);
23808     },
23809
23810     // private
23811     storeOptions : function(o){
23812         o = Roo.apply({}, o);
23813         delete o.callback;
23814         delete o.scope;
23815         this.lastOptions = o;
23816     },
23817
23818     /**
23819      * Loads the Record cache from the configured Proxy using the configured Reader.
23820      * <p>
23821      * If using remote paging, then the first load call must specify the <em>start</em>
23822      * and <em>limit</em> properties in the options.params property to establish the initial
23823      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23824      * <p>
23825      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23826      * and this call will return before the new data has been loaded. Perform any post-processing
23827      * in a callback function, or in a "load" event handler.</strong>
23828      * <p>
23829      * @param {Object} options An object containing properties which control loading options:<ul>
23830      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23831      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23832      * passed the following arguments:<ul>
23833      * <li>r : Roo.data.Record[]</li>
23834      * <li>options: Options object from the load call</li>
23835      * <li>success: Boolean success indicator</li></ul></li>
23836      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23837      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23838      * </ul>
23839      */
23840     load : function(options){
23841         options = options || {};
23842         if(this.fireEvent("beforeload", this, options) !== false){
23843             this.storeOptions(options);
23844             var p = Roo.apply(options.params || {}, this.baseParams);
23845             // if meta was not loaded from remote source.. try requesting it.
23846             if (!this.reader.metaFromRemote) {
23847                 p._requestMeta = 1;
23848             }
23849             if(this.sortInfo && this.remoteSort){
23850                 var pn = this.paramNames;
23851                 p[pn["sort"]] = this.sortInfo.field;
23852                 p[pn["dir"]] = this.sortInfo.direction;
23853             }
23854             if (this.multiSort) {
23855                 var pn = this.paramNames;
23856                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23857             }
23858             
23859             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23860         }
23861     },
23862
23863     /**
23864      * Reloads the Record cache from the configured Proxy using the configured Reader and
23865      * the options from the last load operation performed.
23866      * @param {Object} options (optional) An object containing properties which may override the options
23867      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23868      * the most recently used options are reused).
23869      */
23870     reload : function(options){
23871         this.load(Roo.applyIf(options||{}, this.lastOptions));
23872     },
23873
23874     // private
23875     // Called as a callback by the Reader during a load operation.
23876     loadRecords : function(o, options, success){
23877         if(!o || success === false){
23878             if(success !== false){
23879                 this.fireEvent("load", this, [], options, o);
23880             }
23881             if(options.callback){
23882                 options.callback.call(options.scope || this, [], options, false);
23883             }
23884             return;
23885         }
23886         // if data returned failure - throw an exception.
23887         if (o.success === false) {
23888             // show a message if no listener is registered.
23889             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23890                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23891             }
23892             // loadmask wil be hooked into this..
23893             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23894             return;
23895         }
23896         var r = o.records, t = o.totalRecords || r.length;
23897         
23898         this.fireEvent("beforeloadadd", this, r, options, o);
23899         
23900         if(!options || options.add !== true){
23901             if(this.pruneModifiedRecords){
23902                 this.modified = [];
23903             }
23904             for(var i = 0, len = r.length; i < len; i++){
23905                 r[i].join(this);
23906             }
23907             if(this.snapshot){
23908                 this.data = this.snapshot;
23909                 delete this.snapshot;
23910             }
23911             this.data.clear();
23912             this.data.addAll(r);
23913             this.totalLength = t;
23914             this.applySort();
23915             this.fireEvent("datachanged", this);
23916         }else{
23917             this.totalLength = Math.max(t, this.data.length+r.length);
23918             this.add(r);
23919         }
23920         
23921         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23922                 
23923             var e = new Roo.data.Record({});
23924
23925             e.set(this.parent.displayField, this.parent.emptyTitle);
23926             e.set(this.parent.valueField, '');
23927
23928             this.insert(0, e);
23929         }
23930             
23931         this.fireEvent("load", this, r, options, o);
23932         if(options.callback){
23933             options.callback.call(options.scope || this, r, options, true);
23934         }
23935     },
23936
23937
23938     /**
23939      * Loads data from a passed data block. A Reader which understands the format of the data
23940      * must have been configured in the constructor.
23941      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23942      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23943      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23944      */
23945     loadData : function(o, append){
23946         var r = this.reader.readRecords(o);
23947         this.loadRecords(r, {add: append}, true);
23948     },
23949     
23950      /**
23951      * using 'cn' the nested child reader read the child array into it's child stores.
23952      * @param {Object} rec The record with a 'children array
23953      */
23954     loadDataFromChildren : function(rec)
23955     {
23956         this.loadData(this.reader.toLoadData(rec));
23957     },
23958     
23959
23960     /**
23961      * Gets the number of cached records.
23962      * <p>
23963      * <em>If using paging, this may not be the total size of the dataset. If the data object
23964      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23965      * the data set size</em>
23966      */
23967     getCount : function(){
23968         return this.data.length || 0;
23969     },
23970
23971     /**
23972      * Gets the total number of records in the dataset as returned by the server.
23973      * <p>
23974      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23975      * the dataset size</em>
23976      */
23977     getTotalCount : function(){
23978         return this.totalLength || 0;
23979     },
23980
23981     /**
23982      * Returns the sort state of the Store as an object with two properties:
23983      * <pre><code>
23984  field {String} The name of the field by which the Records are sorted
23985  direction {String} The sort order, "ASC" or "DESC"
23986      * </code></pre>
23987      */
23988     getSortState : function(){
23989         return this.sortInfo;
23990     },
23991
23992     // private
23993     applySort : function(){
23994         if(this.sortInfo && !this.remoteSort){
23995             var s = this.sortInfo, f = s.field;
23996             var st = this.fields.get(f).sortType;
23997             var fn = function(r1, r2){
23998                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23999                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24000             };
24001             this.data.sort(s.direction, fn);
24002             if(this.snapshot && this.snapshot != this.data){
24003                 this.snapshot.sort(s.direction, fn);
24004             }
24005         }
24006     },
24007
24008     /**
24009      * Sets the default sort column and order to be used by the next load operation.
24010      * @param {String} fieldName The name of the field to sort by.
24011      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24012      */
24013     setDefaultSort : function(field, dir){
24014         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24015     },
24016
24017     /**
24018      * Sort the Records.
24019      * If remote sorting is used, the sort is performed on the server, and the cache is
24020      * reloaded. If local sorting is used, the cache is sorted internally.
24021      * @param {String} fieldName The name of the field to sort by.
24022      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24023      */
24024     sort : function(fieldName, dir){
24025         var f = this.fields.get(fieldName);
24026         if(!dir){
24027             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24028             
24029             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24030                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24031             }else{
24032                 dir = f.sortDir;
24033             }
24034         }
24035         this.sortToggle[f.name] = dir;
24036         this.sortInfo = {field: f.name, direction: dir};
24037         if(!this.remoteSort){
24038             this.applySort();
24039             this.fireEvent("datachanged", this);
24040         }else{
24041             this.load(this.lastOptions);
24042         }
24043     },
24044
24045     /**
24046      * Calls the specified function for each of the Records in the cache.
24047      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24048      * Returning <em>false</em> aborts and exits the iteration.
24049      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24050      */
24051     each : function(fn, scope){
24052         this.data.each(fn, scope);
24053     },
24054
24055     /**
24056      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24057      * (e.g., during paging).
24058      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24059      */
24060     getModifiedRecords : function(){
24061         return this.modified;
24062     },
24063
24064     // private
24065     createFilterFn : function(property, value, anyMatch){
24066         if(!value.exec){ // not a regex
24067             value = String(value);
24068             if(value.length == 0){
24069                 return false;
24070             }
24071             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24072         }
24073         return function(r){
24074             return value.test(r.data[property]);
24075         };
24076     },
24077
24078     /**
24079      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24080      * @param {String} property A field on your records
24081      * @param {Number} start The record index to start at (defaults to 0)
24082      * @param {Number} end The last record index to include (defaults to length - 1)
24083      * @return {Number} The sum
24084      */
24085     sum : function(property, start, end){
24086         var rs = this.data.items, v = 0;
24087         start = start || 0;
24088         end = (end || end === 0) ? end : rs.length-1;
24089
24090         for(var i = start; i <= end; i++){
24091             v += (rs[i].data[property] || 0);
24092         }
24093         return v;
24094     },
24095
24096     /**
24097      * Filter the records by a specified property.
24098      * @param {String} field A field on your records
24099      * @param {String/RegExp} value Either a string that the field
24100      * should start with or a RegExp to test against the field
24101      * @param {Boolean} anyMatch True to match any part not just the beginning
24102      */
24103     filter : function(property, value, anyMatch){
24104         var fn = this.createFilterFn(property, value, anyMatch);
24105         return fn ? this.filterBy(fn) : this.clearFilter();
24106     },
24107
24108     /**
24109      * Filter by a function. The specified function will be called with each
24110      * record in this data source. If the function returns true the record is included,
24111      * otherwise it is filtered.
24112      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24113      * @param {Object} scope (optional) The scope of the function (defaults to this)
24114      */
24115     filterBy : function(fn, scope){
24116         this.snapshot = this.snapshot || this.data;
24117         this.data = this.queryBy(fn, scope||this);
24118         this.fireEvent("datachanged", this);
24119     },
24120
24121     /**
24122      * Query the records by a specified property.
24123      * @param {String} field A field on your records
24124      * @param {String/RegExp} value Either a string that the field
24125      * should start with or a RegExp to test against the field
24126      * @param {Boolean} anyMatch True to match any part not just the beginning
24127      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24128      */
24129     query : function(property, value, anyMatch){
24130         var fn = this.createFilterFn(property, value, anyMatch);
24131         return fn ? this.queryBy(fn) : this.data.clone();
24132     },
24133
24134     /**
24135      * Query by a function. The specified function will be called with each
24136      * record in this data source. If the function returns true the record is included
24137      * in the results.
24138      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24139      * @param {Object} scope (optional) The scope of the function (defaults to this)
24140       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24141      **/
24142     queryBy : function(fn, scope){
24143         var data = this.snapshot || this.data;
24144         return data.filterBy(fn, scope||this);
24145     },
24146
24147     /**
24148      * Collects unique values for a particular dataIndex from this store.
24149      * @param {String} dataIndex The property to collect
24150      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24151      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24152      * @return {Array} An array of the unique values
24153      **/
24154     collect : function(dataIndex, allowNull, bypassFilter){
24155         var d = (bypassFilter === true && this.snapshot) ?
24156                 this.snapshot.items : this.data.items;
24157         var v, sv, r = [], l = {};
24158         for(var i = 0, len = d.length; i < len; i++){
24159             v = d[i].data[dataIndex];
24160             sv = String(v);
24161             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24162                 l[sv] = true;
24163                 r[r.length] = v;
24164             }
24165         }
24166         return r;
24167     },
24168
24169     /**
24170      * Revert to a view of the Record cache with no filtering applied.
24171      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24172      */
24173     clearFilter : function(suppressEvent){
24174         if(this.snapshot && this.snapshot != this.data){
24175             this.data = this.snapshot;
24176             delete this.snapshot;
24177             if(suppressEvent !== true){
24178                 this.fireEvent("datachanged", this);
24179             }
24180         }
24181     },
24182
24183     // private
24184     afterEdit : function(record){
24185         if(this.modified.indexOf(record) == -1){
24186             this.modified.push(record);
24187         }
24188         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24189     },
24190     
24191     // private
24192     afterReject : function(record){
24193         this.modified.remove(record);
24194         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24195     },
24196
24197     // private
24198     afterCommit : function(record){
24199         this.modified.remove(record);
24200         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24201     },
24202
24203     /**
24204      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24205      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24206      */
24207     commitChanges : function(){
24208         var m = this.modified.slice(0);
24209         this.modified = [];
24210         for(var i = 0, len = m.length; i < len; i++){
24211             m[i].commit();
24212         }
24213     },
24214
24215     /**
24216      * Cancel outstanding changes on all changed records.
24217      */
24218     rejectChanges : function(){
24219         var m = this.modified.slice(0);
24220         this.modified = [];
24221         for(var i = 0, len = m.length; i < len; i++){
24222             m[i].reject();
24223         }
24224     },
24225
24226     onMetaChange : function(meta, rtype, o){
24227         this.recordType = rtype;
24228         this.fields = rtype.prototype.fields;
24229         delete this.snapshot;
24230         this.sortInfo = meta.sortInfo || this.sortInfo;
24231         this.modified = [];
24232         this.fireEvent('metachange', this, this.reader.meta);
24233     },
24234     
24235     moveIndex : function(data, type)
24236     {
24237         var index = this.indexOf(data);
24238         
24239         var newIndex = index + type;
24240         
24241         this.remove(data);
24242         
24243         this.insert(newIndex, data);
24244         
24245     }
24246 });/*
24247  * Based on:
24248  * Ext JS Library 1.1.1
24249  * Copyright(c) 2006-2007, Ext JS, LLC.
24250  *
24251  * Originally Released Under LGPL - original licence link has changed is not relivant.
24252  *
24253  * Fork - LGPL
24254  * <script type="text/javascript">
24255  */
24256
24257 /**
24258  * @class Roo.data.SimpleStore
24259  * @extends Roo.data.Store
24260  * Small helper class to make creating Stores from Array data easier.
24261  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24262  * @cfg {Array} fields An array of field definition objects, or field name strings.
24263  * @cfg {Object} an existing reader (eg. copied from another store)
24264  * @cfg {Array} data The multi-dimensional array of data
24265  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24266  * @cfg {Roo.data.Reader} reader  [not-required] 
24267  * @constructor
24268  * @param {Object} config
24269  */
24270 Roo.data.SimpleStore = function(config)
24271 {
24272     Roo.data.SimpleStore.superclass.constructor.call(this, {
24273         isLocal : true,
24274         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24275                 id: config.id
24276             },
24277             Roo.data.Record.create(config.fields)
24278         ),
24279         proxy : new Roo.data.MemoryProxy(config.data)
24280     });
24281     this.load();
24282 };
24283 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24284  * Based on:
24285  * Ext JS Library 1.1.1
24286  * Copyright(c) 2006-2007, Ext JS, LLC.
24287  *
24288  * Originally Released Under LGPL - original licence link has changed is not relivant.
24289  *
24290  * Fork - LGPL
24291  * <script type="text/javascript">
24292  */
24293
24294 /**
24295 /**
24296  * @extends Roo.data.Store
24297  * @class Roo.data.JsonStore
24298  * Small helper class to make creating Stores for JSON data easier. <br/>
24299 <pre><code>
24300 var store = new Roo.data.JsonStore({
24301     url: 'get-images.php',
24302     root: 'images',
24303     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24304 });
24305 </code></pre>
24306  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24307  * JsonReader and HttpProxy (unless inline data is provided).</b>
24308  * @cfg {Array} fields An array of field definition objects, or field name strings.
24309  * @constructor
24310  * @param {Object} config
24311  */
24312 Roo.data.JsonStore = function(c){
24313     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24314         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24315         reader: new Roo.data.JsonReader(c, c.fields)
24316     }));
24317 };
24318 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24319  * Based on:
24320  * Ext JS Library 1.1.1
24321  * Copyright(c) 2006-2007, Ext JS, LLC.
24322  *
24323  * Originally Released Under LGPL - original licence link has changed is not relivant.
24324  *
24325  * Fork - LGPL
24326  * <script type="text/javascript">
24327  */
24328
24329  
24330 Roo.data.Field = function(config){
24331     if(typeof config == "string"){
24332         config = {name: config};
24333     }
24334     Roo.apply(this, config);
24335     
24336     if(!this.type){
24337         this.type = "auto";
24338     }
24339     
24340     var st = Roo.data.SortTypes;
24341     // named sortTypes are supported, here we look them up
24342     if(typeof this.sortType == "string"){
24343         this.sortType = st[this.sortType];
24344     }
24345     
24346     // set default sortType for strings and dates
24347     if(!this.sortType){
24348         switch(this.type){
24349             case "string":
24350                 this.sortType = st.asUCString;
24351                 break;
24352             case "date":
24353                 this.sortType = st.asDate;
24354                 break;
24355             default:
24356                 this.sortType = st.none;
24357         }
24358     }
24359
24360     // define once
24361     var stripRe = /[\$,%]/g;
24362
24363     // prebuilt conversion function for this field, instead of
24364     // switching every time we're reading a value
24365     if(!this.convert){
24366         var cv, dateFormat = this.dateFormat;
24367         switch(this.type){
24368             case "":
24369             case "auto":
24370             case undefined:
24371                 cv = function(v){ return v; };
24372                 break;
24373             case "string":
24374                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24375                 break;
24376             case "int":
24377                 cv = function(v){
24378                     return v !== undefined && v !== null && v !== '' ?
24379                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24380                     };
24381                 break;
24382             case "float":
24383                 cv = function(v){
24384                     return v !== undefined && v !== null && v !== '' ?
24385                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24386                     };
24387                 break;
24388             case "bool":
24389             case "boolean":
24390                 cv = function(v){ return v === true || v === "true" || v == 1; };
24391                 break;
24392             case "date":
24393                 cv = function(v){
24394                     if(!v){
24395                         return '';
24396                     }
24397                     if(v instanceof Date){
24398                         return v;
24399                     }
24400                     if(dateFormat){
24401                         if(dateFormat == "timestamp"){
24402                             return new Date(v*1000);
24403                         }
24404                         return Date.parseDate(v, dateFormat);
24405                     }
24406                     var parsed = Date.parse(v);
24407                     return parsed ? new Date(parsed) : null;
24408                 };
24409              break;
24410             
24411         }
24412         this.convert = cv;
24413     }
24414 };
24415
24416 Roo.data.Field.prototype = {
24417     dateFormat: null,
24418     defaultValue: "",
24419     mapping: null,
24420     sortType : null,
24421     sortDir : "ASC"
24422 };/*
24423  * Based on:
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *
24427  * Originally Released Under LGPL - original licence link has changed is not relivant.
24428  *
24429  * Fork - LGPL
24430  * <script type="text/javascript">
24431  */
24432  
24433 // Base class for reading structured data from a data source.  This class is intended to be
24434 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24435
24436 /**
24437  * @class Roo.data.DataReader
24438  * Base class for reading structured data from a data source.  This class is intended to be
24439  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24440  */
24441
24442 Roo.data.DataReader = function(meta, recordType){
24443     
24444     this.meta = meta;
24445     
24446     this.recordType = recordType instanceof Array ? 
24447         Roo.data.Record.create(recordType) : recordType;
24448 };
24449
24450 Roo.data.DataReader.prototype = {
24451     
24452     
24453     readerType : 'Data',
24454      /**
24455      * Create an empty record
24456      * @param {Object} data (optional) - overlay some values
24457      * @return {Roo.data.Record} record created.
24458      */
24459     newRow :  function(d) {
24460         var da =  {};
24461         this.recordType.prototype.fields.each(function(c) {
24462             switch( c.type) {
24463                 case 'int' : da[c.name] = 0; break;
24464                 case 'date' : da[c.name] = new Date(); break;
24465                 case 'float' : da[c.name] = 0.0; break;
24466                 case 'boolean' : da[c.name] = false; break;
24467                 default : da[c.name] = ""; break;
24468             }
24469             
24470         });
24471         return new this.recordType(Roo.apply(da, d));
24472     }
24473     
24474     
24475 };/*
24476  * Based on:
24477  * Ext JS Library 1.1.1
24478  * Copyright(c) 2006-2007, Ext JS, LLC.
24479  *
24480  * Originally Released Under LGPL - original licence link has changed is not relivant.
24481  *
24482  * Fork - LGPL
24483  * <script type="text/javascript">
24484  */
24485
24486 /**
24487  * @class Roo.data.DataProxy
24488  * @extends Roo.data.Observable
24489  * @abstract
24490  * This class is an abstract base class for implementations which provide retrieval of
24491  * unformatted data objects.<br>
24492  * <p>
24493  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24494  * (of the appropriate type which knows how to parse the data object) to provide a block of
24495  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24496  * <p>
24497  * Custom implementations must implement the load method as described in
24498  * {@link Roo.data.HttpProxy#load}.
24499  */
24500 Roo.data.DataProxy = function(){
24501     this.addEvents({
24502         /**
24503          * @event beforeload
24504          * Fires before a network request is made to retrieve a data object.
24505          * @param {Object} This DataProxy object.
24506          * @param {Object} params The params parameter to the load function.
24507          */
24508         beforeload : true,
24509         /**
24510          * @event load
24511          * Fires before the load method's callback is called.
24512          * @param {Object} This DataProxy object.
24513          * @param {Object} o The data object.
24514          * @param {Object} arg The callback argument object passed to the load function.
24515          */
24516         load : true,
24517         /**
24518          * @event loadexception
24519          * Fires if an Exception occurs during data retrieval.
24520          * @param {Object} This DataProxy object.
24521          * @param {Object} o The data object.
24522          * @param {Object} arg The callback argument object passed to the load function.
24523          * @param {Object} e The Exception.
24524          */
24525         loadexception : true
24526     });
24527     Roo.data.DataProxy.superclass.constructor.call(this);
24528 };
24529
24530 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24531
24532     /**
24533      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24534      */
24535 /*
24536  * Based on:
24537  * Ext JS Library 1.1.1
24538  * Copyright(c) 2006-2007, Ext JS, LLC.
24539  *
24540  * Originally Released Under LGPL - original licence link has changed is not relivant.
24541  *
24542  * Fork - LGPL
24543  * <script type="text/javascript">
24544  */
24545 /**
24546  * @class Roo.data.MemoryProxy
24547  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24548  * to the Reader when its load method is called.
24549  * @constructor
24550  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24551  */
24552 Roo.data.MemoryProxy = function(data){
24553     if (data.data) {
24554         data = data.data;
24555     }
24556     Roo.data.MemoryProxy.superclass.constructor.call(this);
24557     this.data = data;
24558 };
24559
24560 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24561     
24562     /**
24563      * Load data from the requested source (in this case an in-memory
24564      * data object passed to the constructor), read the data object into
24565      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24566      * process that block using the passed callback.
24567      * @param {Object} params This parameter is not used by the MemoryProxy class.
24568      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24569      * object into a block of Roo.data.Records.
24570      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24571      * The function must be passed <ul>
24572      * <li>The Record block object</li>
24573      * <li>The "arg" argument from the load function</li>
24574      * <li>A boolean success indicator</li>
24575      * </ul>
24576      * @param {Object} scope The scope in which to call the callback
24577      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24578      */
24579     load : function(params, reader, callback, scope, arg){
24580         params = params || {};
24581         var result;
24582         try {
24583             result = reader.readRecords(params.data ? params.data :this.data);
24584         }catch(e){
24585             this.fireEvent("loadexception", this, arg, null, e);
24586             callback.call(scope, null, arg, false);
24587             return;
24588         }
24589         callback.call(scope, result, arg, true);
24590     },
24591     
24592     // private
24593     update : function(params, records){
24594         
24595     }
24596 });/*
24597  * Based on:
24598  * Ext JS Library 1.1.1
24599  * Copyright(c) 2006-2007, Ext JS, LLC.
24600  *
24601  * Originally Released Under LGPL - original licence link has changed is not relivant.
24602  *
24603  * Fork - LGPL
24604  * <script type="text/javascript">
24605  */
24606 /**
24607  * @class Roo.data.HttpProxy
24608  * @extends Roo.data.DataProxy
24609  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24610  * configured to reference a certain URL.<br><br>
24611  * <p>
24612  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24613  * from which the running page was served.<br><br>
24614  * <p>
24615  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24616  * <p>
24617  * Be aware that to enable the browser to parse an XML document, the server must set
24618  * the Content-Type header in the HTTP response to "text/xml".
24619  * @constructor
24620  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24621  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24622  * will be used to make the request.
24623  */
24624 Roo.data.HttpProxy = function(conn){
24625     Roo.data.HttpProxy.superclass.constructor.call(this);
24626     // is conn a conn config or a real conn?
24627     this.conn = conn;
24628     this.useAjax = !conn || !conn.events;
24629   
24630 };
24631
24632 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24633     // thse are take from connection...
24634     
24635     /**
24636      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24637      */
24638     /**
24639      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24640      * extra parameters to each request made by this object. (defaults to undefined)
24641      */
24642     /**
24643      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24644      *  to each request made by this object. (defaults to undefined)
24645      */
24646     /**
24647      * @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)
24648      */
24649     /**
24650      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24651      */
24652      /**
24653      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24654      * @type Boolean
24655      */
24656   
24657
24658     /**
24659      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24660      * @type Boolean
24661      */
24662     /**
24663      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24664      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24665      * a finer-grained basis than the DataProxy events.
24666      */
24667     getConnection : function(){
24668         return this.useAjax ? Roo.Ajax : this.conn;
24669     },
24670
24671     /**
24672      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24673      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24674      * process that block using the passed callback.
24675      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24676      * for the request to the remote server.
24677      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24678      * object into a block of Roo.data.Records.
24679      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24680      * The function must be passed <ul>
24681      * <li>The Record block object</li>
24682      * <li>The "arg" argument from the load function</li>
24683      * <li>A boolean success indicator</li>
24684      * </ul>
24685      * @param {Object} scope The scope in which to call the callback
24686      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24687      */
24688     load : function(params, reader, callback, scope, arg){
24689         if(this.fireEvent("beforeload", this, params) !== false){
24690             var  o = {
24691                 params : params || {},
24692                 request: {
24693                     callback : callback,
24694                     scope : scope,
24695                     arg : arg
24696                 },
24697                 reader: reader,
24698                 callback : this.loadResponse,
24699                 scope: this
24700             };
24701             if(this.useAjax){
24702                 Roo.applyIf(o, this.conn);
24703                 if(this.activeRequest){
24704                     Roo.Ajax.abort(this.activeRequest);
24705                 }
24706                 this.activeRequest = Roo.Ajax.request(o);
24707             }else{
24708                 this.conn.request(o);
24709             }
24710         }else{
24711             callback.call(scope||this, null, arg, false);
24712         }
24713     },
24714
24715     // private
24716     loadResponse : function(o, success, response){
24717         delete this.activeRequest;
24718         if(!success){
24719             this.fireEvent("loadexception", this, o, response);
24720             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24721             return;
24722         }
24723         var result;
24724         try {
24725             result = o.reader.read(response);
24726         }catch(e){
24727             this.fireEvent("loadexception", this, o, response, e);
24728             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24729             return;
24730         }
24731         
24732         this.fireEvent("load", this, o, o.request.arg);
24733         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24734     },
24735
24736     // private
24737     update : function(dataSet){
24738
24739     },
24740
24741     // private
24742     updateResponse : function(dataSet){
24743
24744     }
24745 });/*
24746  * Based on:
24747  * Ext JS Library 1.1.1
24748  * Copyright(c) 2006-2007, Ext JS, LLC.
24749  *
24750  * Originally Released Under LGPL - original licence link has changed is not relivant.
24751  *
24752  * Fork - LGPL
24753  * <script type="text/javascript">
24754  */
24755
24756 /**
24757  * @class Roo.data.ScriptTagProxy
24758  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24759  * other than the originating domain of the running page.<br><br>
24760  * <p>
24761  * <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
24762  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24763  * <p>
24764  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24765  * source code that is used as the source inside a &lt;script> tag.<br><br>
24766  * <p>
24767  * In order for the browser to process the returned data, the server must wrap the data object
24768  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24769  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24770  * depending on whether the callback name was passed:
24771  * <p>
24772  * <pre><code>
24773 boolean scriptTag = false;
24774 String cb = request.getParameter("callback");
24775 if (cb != null) {
24776     scriptTag = true;
24777     response.setContentType("text/javascript");
24778 } else {
24779     response.setContentType("application/x-json");
24780 }
24781 Writer out = response.getWriter();
24782 if (scriptTag) {
24783     out.write(cb + "(");
24784 }
24785 out.print(dataBlock.toJsonString());
24786 if (scriptTag) {
24787     out.write(");");
24788 }
24789 </pre></code>
24790  *
24791  * @constructor
24792  * @param {Object} config A configuration object.
24793  */
24794 Roo.data.ScriptTagProxy = function(config){
24795     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24796     Roo.apply(this, config);
24797     this.head = document.getElementsByTagName("head")[0];
24798 };
24799
24800 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24801
24802 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24803     /**
24804      * @cfg {String} url The URL from which to request the data object.
24805      */
24806     /**
24807      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24808      */
24809     timeout : 30000,
24810     /**
24811      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24812      * the server the name of the callback function set up by the load call to process the returned data object.
24813      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24814      * javascript output which calls this named function passing the data object as its only parameter.
24815      */
24816     callbackParam : "callback",
24817     /**
24818      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24819      * name to the request.
24820      */
24821     nocache : true,
24822
24823     /**
24824      * Load data from the configured URL, read the data object into
24825      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24826      * process that block using the passed callback.
24827      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24828      * for the request to the remote server.
24829      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24830      * object into a block of Roo.data.Records.
24831      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24832      * The function must be passed <ul>
24833      * <li>The Record block object</li>
24834      * <li>The "arg" argument from the load function</li>
24835      * <li>A boolean success indicator</li>
24836      * </ul>
24837      * @param {Object} scope The scope in which to call the callback
24838      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24839      */
24840     load : function(params, reader, callback, scope, arg){
24841         if(this.fireEvent("beforeload", this, params) !== false){
24842
24843             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24844
24845             var url = this.url;
24846             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24847             if(this.nocache){
24848                 url += "&_dc=" + (new Date().getTime());
24849             }
24850             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24851             var trans = {
24852                 id : transId,
24853                 cb : "stcCallback"+transId,
24854                 scriptId : "stcScript"+transId,
24855                 params : params,
24856                 arg : arg,
24857                 url : url,
24858                 callback : callback,
24859                 scope : scope,
24860                 reader : reader
24861             };
24862             var conn = this;
24863
24864             window[trans.cb] = function(o){
24865                 conn.handleResponse(o, trans);
24866             };
24867
24868             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24869
24870             if(this.autoAbort !== false){
24871                 this.abort();
24872             }
24873
24874             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24875
24876             var script = document.createElement("script");
24877             script.setAttribute("src", url);
24878             script.setAttribute("type", "text/javascript");
24879             script.setAttribute("id", trans.scriptId);
24880             this.head.appendChild(script);
24881
24882             this.trans = trans;
24883         }else{
24884             callback.call(scope||this, null, arg, false);
24885         }
24886     },
24887
24888     // private
24889     isLoading : function(){
24890         return this.trans ? true : false;
24891     },
24892
24893     /**
24894      * Abort the current server request.
24895      */
24896     abort : function(){
24897         if(this.isLoading()){
24898             this.destroyTrans(this.trans);
24899         }
24900     },
24901
24902     // private
24903     destroyTrans : function(trans, isLoaded){
24904         this.head.removeChild(document.getElementById(trans.scriptId));
24905         clearTimeout(trans.timeoutId);
24906         if(isLoaded){
24907             window[trans.cb] = undefined;
24908             try{
24909                 delete window[trans.cb];
24910             }catch(e){}
24911         }else{
24912             // if hasn't been loaded, wait for load to remove it to prevent script error
24913             window[trans.cb] = function(){
24914                 window[trans.cb] = undefined;
24915                 try{
24916                     delete window[trans.cb];
24917                 }catch(e){}
24918             };
24919         }
24920     },
24921
24922     // private
24923     handleResponse : function(o, trans){
24924         this.trans = false;
24925         this.destroyTrans(trans, true);
24926         var result;
24927         try {
24928             result = trans.reader.readRecords(o);
24929         }catch(e){
24930             this.fireEvent("loadexception", this, o, trans.arg, e);
24931             trans.callback.call(trans.scope||window, null, trans.arg, false);
24932             return;
24933         }
24934         this.fireEvent("load", this, o, trans.arg);
24935         trans.callback.call(trans.scope||window, result, trans.arg, true);
24936     },
24937
24938     // private
24939     handleFailure : function(trans){
24940         this.trans = false;
24941         this.destroyTrans(trans, false);
24942         this.fireEvent("loadexception", this, null, trans.arg);
24943         trans.callback.call(trans.scope||window, null, trans.arg, false);
24944     }
24945 });/*
24946  * Based on:
24947  * Ext JS Library 1.1.1
24948  * Copyright(c) 2006-2007, Ext JS, LLC.
24949  *
24950  * Originally Released Under LGPL - original licence link has changed is not relivant.
24951  *
24952  * Fork - LGPL
24953  * <script type="text/javascript">
24954  */
24955
24956 /**
24957  * @class Roo.data.JsonReader
24958  * @extends Roo.data.DataReader
24959  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24960  * based on mappings in a provided Roo.data.Record constructor.
24961  * 
24962  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24963  * in the reply previously. 
24964  * 
24965  * <p>
24966  * Example code:
24967  * <pre><code>
24968 var RecordDef = Roo.data.Record.create([
24969     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24970     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24971 ]);
24972 var myReader = new Roo.data.JsonReader({
24973     totalProperty: "results",    // The property which contains the total dataset size (optional)
24974     root: "rows",                // The property which contains an Array of row objects
24975     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24976 }, RecordDef);
24977 </code></pre>
24978  * <p>
24979  * This would consume a JSON file like this:
24980  * <pre><code>
24981 { 'results': 2, 'rows': [
24982     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24983     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24984 }
24985 </code></pre>
24986  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24987  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24988  * paged from the remote server.
24989  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24990  * @cfg {String} root name of the property which contains the Array of row objects.
24991  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24992  * @cfg {Array} fields Array of field definition objects
24993  * @constructor
24994  * Create a new JsonReader
24995  * @param {Object} meta Metadata configuration options
24996  * @param {Object} recordType Either an Array of field definition objects,
24997  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24998  */
24999 Roo.data.JsonReader = function(meta, recordType){
25000     
25001     meta = meta || {};
25002     // set some defaults:
25003     Roo.applyIf(meta, {
25004         totalProperty: 'total',
25005         successProperty : 'success',
25006         root : 'data',
25007         id : 'id'
25008     });
25009     
25010     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25011 };
25012 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25013     
25014     readerType : 'Json',
25015     
25016     /**
25017      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25018      * Used by Store query builder to append _requestMeta to params.
25019      * 
25020      */
25021     metaFromRemote : false,
25022     /**
25023      * This method is only used by a DataProxy which has retrieved data from a remote server.
25024      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25025      * @return {Object} data A data block which is used by an Roo.data.Store object as
25026      * a cache of Roo.data.Records.
25027      */
25028     read : function(response){
25029         var json = response.responseText;
25030        
25031         var o = /* eval:var:o */ eval("("+json+")");
25032         if(!o) {
25033             throw {message: "JsonReader.read: Json object not found"};
25034         }
25035         
25036         if(o.metaData){
25037             
25038             delete this.ef;
25039             this.metaFromRemote = true;
25040             this.meta = o.metaData;
25041             this.recordType = Roo.data.Record.create(o.metaData.fields);
25042             this.onMetaChange(this.meta, this.recordType, o);
25043         }
25044         return this.readRecords(o);
25045     },
25046
25047     // private function a store will implement
25048     onMetaChange : function(meta, recordType, o){
25049
25050     },
25051
25052     /**
25053          * @ignore
25054          */
25055     simpleAccess: function(obj, subsc) {
25056         return obj[subsc];
25057     },
25058
25059         /**
25060          * @ignore
25061          */
25062     getJsonAccessor: function(){
25063         var re = /[\[\.]/;
25064         return function(expr) {
25065             try {
25066                 return(re.test(expr))
25067                     ? new Function("obj", "return obj." + expr)
25068                     : function(obj){
25069                         return obj[expr];
25070                     };
25071             } catch(e){}
25072             return Roo.emptyFn;
25073         };
25074     }(),
25075
25076     /**
25077      * Create a data block containing Roo.data.Records from an XML document.
25078      * @param {Object} o An object which contains an Array of row objects in the property specified
25079      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25080      * which contains the total size of the dataset.
25081      * @return {Object} data A data block which is used by an Roo.data.Store object as
25082      * a cache of Roo.data.Records.
25083      */
25084     readRecords : function(o){
25085         /**
25086          * After any data loads, the raw JSON data is available for further custom processing.
25087          * @type Object
25088          */
25089         this.o = o;
25090         var s = this.meta, Record = this.recordType,
25091             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25092
25093 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25094         if (!this.ef) {
25095             if(s.totalProperty) {
25096                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25097                 }
25098                 if(s.successProperty) {
25099                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25100                 }
25101                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25102                 if (s.id) {
25103                         var g = this.getJsonAccessor(s.id);
25104                         this.getId = function(rec) {
25105                                 var r = g(rec);  
25106                                 return (r === undefined || r === "") ? null : r;
25107                         };
25108                 } else {
25109                         this.getId = function(){return null;};
25110                 }
25111             this.ef = [];
25112             for(var jj = 0; jj < fl; jj++){
25113                 f = fi[jj];
25114                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25115                 this.ef[jj] = this.getJsonAccessor(map);
25116             }
25117         }
25118
25119         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25120         if(s.totalProperty){
25121             var vt = parseInt(this.getTotal(o), 10);
25122             if(!isNaN(vt)){
25123                 totalRecords = vt;
25124             }
25125         }
25126         if(s.successProperty){
25127             var vs = this.getSuccess(o);
25128             if(vs === false || vs === 'false'){
25129                 success = false;
25130             }
25131         }
25132         var records = [];
25133         for(var i = 0; i < c; i++){
25134                 var n = root[i];
25135             var values = {};
25136             var id = this.getId(n);
25137             for(var j = 0; j < fl; j++){
25138                 f = fi[j];
25139             var v = this.ef[j](n);
25140             if (!f.convert) {
25141                 Roo.log('missing convert for ' + f.name);
25142                 Roo.log(f);
25143                 continue;
25144             }
25145             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25146             }
25147             var record = new Record(values, id);
25148             record.json = n;
25149             records[i] = record;
25150         }
25151         return {
25152             raw : o,
25153             success : success,
25154             records : records,
25155             totalRecords : totalRecords
25156         };
25157     },
25158     // used when loading children.. @see loadDataFromChildren
25159     toLoadData: function(rec)
25160     {
25161         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25162         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25163         return { data : data, total : data.length };
25164         
25165     }
25166 });/*
25167  * Based on:
25168  * Ext JS Library 1.1.1
25169  * Copyright(c) 2006-2007, Ext JS, LLC.
25170  *
25171  * Originally Released Under LGPL - original licence link has changed is not relivant.
25172  *
25173  * Fork - LGPL
25174  * <script type="text/javascript">
25175  */
25176
25177 /**
25178  * @class Roo.data.XmlReader
25179  * @extends Roo.data.DataReader
25180  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25181  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25182  * <p>
25183  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25184  * header in the HTTP response must be set to "text/xml".</em>
25185  * <p>
25186  * Example code:
25187  * <pre><code>
25188 var RecordDef = Roo.data.Record.create([
25189    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25190    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25191 ]);
25192 var myReader = new Roo.data.XmlReader({
25193    totalRecords: "results", // The element which contains the total dataset size (optional)
25194    record: "row",           // The repeated element which contains row information
25195    id: "id"                 // The element within the row that provides an ID for the record (optional)
25196 }, RecordDef);
25197 </code></pre>
25198  * <p>
25199  * This would consume an XML file like this:
25200  * <pre><code>
25201 &lt;?xml?>
25202 &lt;dataset>
25203  &lt;results>2&lt;/results>
25204  &lt;row>
25205    &lt;id>1&lt;/id>
25206    &lt;name>Bill&lt;/name>
25207    &lt;occupation>Gardener&lt;/occupation>
25208  &lt;/row>
25209  &lt;row>
25210    &lt;id>2&lt;/id>
25211    &lt;name>Ben&lt;/name>
25212    &lt;occupation>Horticulturalist&lt;/occupation>
25213  &lt;/row>
25214 &lt;/dataset>
25215 </code></pre>
25216  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25217  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25218  * paged from the remote server.
25219  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25220  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25221  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25222  * a record identifier value.
25223  * @constructor
25224  * Create a new XmlReader
25225  * @param {Object} meta Metadata configuration options
25226  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25227  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25228  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25229  */
25230 Roo.data.XmlReader = function(meta, recordType){
25231     meta = meta || {};
25232     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25233 };
25234 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25235     
25236     readerType : 'Xml',
25237     
25238     /**
25239      * This method is only used by a DataProxy which has retrieved data from a remote server.
25240          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25241          * to contain a method called 'responseXML' that returns an XML document object.
25242      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25243      * a cache of Roo.data.Records.
25244      */
25245     read : function(response){
25246         var doc = response.responseXML;
25247         if(!doc) {
25248             throw {message: "XmlReader.read: XML Document not available"};
25249         }
25250         return this.readRecords(doc);
25251     },
25252
25253     /**
25254      * Create a data block containing Roo.data.Records from an XML document.
25255          * @param {Object} doc A parsed XML document.
25256      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25257      * a cache of Roo.data.Records.
25258      */
25259     readRecords : function(doc){
25260         /**
25261          * After any data loads/reads, the raw XML Document is available for further custom processing.
25262          * @type XMLDocument
25263          */
25264         this.xmlData = doc;
25265         var root = doc.documentElement || doc;
25266         var q = Roo.DomQuery;
25267         var recordType = this.recordType, fields = recordType.prototype.fields;
25268         var sid = this.meta.id;
25269         var totalRecords = 0, success = true;
25270         if(this.meta.totalRecords){
25271             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25272         }
25273         
25274         if(this.meta.success){
25275             var sv = q.selectValue(this.meta.success, root, true);
25276             success = sv !== false && sv !== 'false';
25277         }
25278         var records = [];
25279         var ns = q.select(this.meta.record, root);
25280         for(var i = 0, len = ns.length; i < len; i++) {
25281                 var n = ns[i];
25282                 var values = {};
25283                 var id = sid ? q.selectValue(sid, n) : undefined;
25284                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25285                     var f = fields.items[j];
25286                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25287                     v = f.convert(v);
25288                     values[f.name] = v;
25289                 }
25290                 var record = new recordType(values, id);
25291                 record.node = n;
25292                 records[records.length] = record;
25293             }
25294
25295             return {
25296                 success : success,
25297                 records : records,
25298                 totalRecords : totalRecords || records.length
25299             };
25300     }
25301 });/*
25302  * Based on:
25303  * Ext JS Library 1.1.1
25304  * Copyright(c) 2006-2007, Ext JS, LLC.
25305  *
25306  * Originally Released Under LGPL - original licence link has changed is not relivant.
25307  *
25308  * Fork - LGPL
25309  * <script type="text/javascript">
25310  */
25311
25312 /**
25313  * @class Roo.data.ArrayReader
25314  * @extends Roo.data.DataReader
25315  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25316  * Each element of that Array represents a row of data fields. The
25317  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25318  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25319  * <p>
25320  * Example code:.
25321  * <pre><code>
25322 var RecordDef = Roo.data.Record.create([
25323     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25324     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25325 ]);
25326 var myReader = new Roo.data.ArrayReader({
25327     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25328 }, RecordDef);
25329 </code></pre>
25330  * <p>
25331  * This would consume an Array like this:
25332  * <pre><code>
25333 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25334   </code></pre>
25335  
25336  * @constructor
25337  * Create a new JsonReader
25338  * @param {Object} meta Metadata configuration options.
25339  * @param {Object|Array} recordType Either an Array of field definition objects
25340  * 
25341  * @cfg {Array} fields Array of field definition objects
25342  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25343  * as specified to {@link Roo.data.Record#create},
25344  * or an {@link Roo.data.Record} object
25345  *
25346  * 
25347  * created using {@link Roo.data.Record#create}.
25348  */
25349 Roo.data.ArrayReader = function(meta, recordType)
25350 {    
25351     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25352 };
25353
25354 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25355     
25356       /**
25357      * Create a data block containing Roo.data.Records from an XML document.
25358      * @param {Object} o An Array of row objects which represents the dataset.
25359      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25360      * a cache of Roo.data.Records.
25361      */
25362     readRecords : function(o)
25363     {
25364         var sid = this.meta ? this.meta.id : null;
25365         var recordType = this.recordType, fields = recordType.prototype.fields;
25366         var records = [];
25367         var root = o;
25368         for(var i = 0; i < root.length; i++){
25369             var n = root[i];
25370             var values = {};
25371             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25372             for(var j = 0, jlen = fields.length; j < jlen; j++){
25373                 var f = fields.items[j];
25374                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25375                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25376                 v = f.convert(v);
25377                 values[f.name] = v;
25378             }
25379             var record = new recordType(values, id);
25380             record.json = n;
25381             records[records.length] = record;
25382         }
25383         return {
25384             records : records,
25385             totalRecords : records.length
25386         };
25387     },
25388     // used when loading children.. @see loadDataFromChildren
25389     toLoadData: function(rec)
25390     {
25391         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25392         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25393         
25394     }
25395     
25396     
25397 });/*
25398  * Based on:
25399  * Ext JS Library 1.1.1
25400  * Copyright(c) 2006-2007, Ext JS, LLC.
25401  *
25402  * Originally Released Under LGPL - original licence link has changed is not relivant.
25403  *
25404  * Fork - LGPL
25405  * <script type="text/javascript">
25406  */
25407
25408
25409 /**
25410  * @class Roo.data.Tree
25411  * @extends Roo.util.Observable
25412  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25413  * in the tree have most standard DOM functionality.
25414  * @constructor
25415  * @param {Node} root (optional) The root node
25416  */
25417 Roo.data.Tree = function(root){
25418    this.nodeHash = {};
25419    /**
25420     * The root node for this tree
25421     * @type Node
25422     */
25423    this.root = null;
25424    if(root){
25425        this.setRootNode(root);
25426    }
25427    this.addEvents({
25428        /**
25429         * @event append
25430         * Fires when a new child node is appended to a node in this tree.
25431         * @param {Tree} tree The owner tree
25432         * @param {Node} parent The parent node
25433         * @param {Node} node The newly appended node
25434         * @param {Number} index The index of the newly appended node
25435         */
25436        "append" : true,
25437        /**
25438         * @event remove
25439         * Fires when a child node is removed from a node in this tree.
25440         * @param {Tree} tree The owner tree
25441         * @param {Node} parent The parent node
25442         * @param {Node} node The child node removed
25443         */
25444        "remove" : true,
25445        /**
25446         * @event move
25447         * Fires when a node is moved to a new location in the tree
25448         * @param {Tree} tree The owner tree
25449         * @param {Node} node The node moved
25450         * @param {Node} oldParent The old parent of this node
25451         * @param {Node} newParent The new parent of this node
25452         * @param {Number} index The index it was moved to
25453         */
25454        "move" : true,
25455        /**
25456         * @event insert
25457         * Fires when a new child node is inserted in a node in this tree.
25458         * @param {Tree} tree The owner tree
25459         * @param {Node} parent The parent node
25460         * @param {Node} node The child node inserted
25461         * @param {Node} refNode The child node the node was inserted before
25462         */
25463        "insert" : true,
25464        /**
25465         * @event beforeappend
25466         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25467         * @param {Tree} tree The owner tree
25468         * @param {Node} parent The parent node
25469         * @param {Node} node The child node to be appended
25470         */
25471        "beforeappend" : true,
25472        /**
25473         * @event beforeremove
25474         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25475         * @param {Tree} tree The owner tree
25476         * @param {Node} parent The parent node
25477         * @param {Node} node The child node to be removed
25478         */
25479        "beforeremove" : true,
25480        /**
25481         * @event beforemove
25482         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25483         * @param {Tree} tree The owner tree
25484         * @param {Node} node The node being moved
25485         * @param {Node} oldParent The parent of the node
25486         * @param {Node} newParent The new parent the node is moving to
25487         * @param {Number} index The index it is being moved to
25488         */
25489        "beforemove" : true,
25490        /**
25491         * @event beforeinsert
25492         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25493         * @param {Tree} tree The owner tree
25494         * @param {Node} parent The parent node
25495         * @param {Node} node The child node to be inserted
25496         * @param {Node} refNode The child node the node is being inserted before
25497         */
25498        "beforeinsert" : true
25499    });
25500
25501     Roo.data.Tree.superclass.constructor.call(this);
25502 };
25503
25504 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25505     pathSeparator: "/",
25506
25507     proxyNodeEvent : function(){
25508         return this.fireEvent.apply(this, arguments);
25509     },
25510
25511     /**
25512      * Returns the root node for this tree.
25513      * @return {Node}
25514      */
25515     getRootNode : function(){
25516         return this.root;
25517     },
25518
25519     /**
25520      * Sets the root node for this tree.
25521      * @param {Node} node
25522      * @return {Node}
25523      */
25524     setRootNode : function(node){
25525         this.root = node;
25526         node.ownerTree = this;
25527         node.isRoot = true;
25528         this.registerNode(node);
25529         return node;
25530     },
25531
25532     /**
25533      * Gets a node in this tree by its id.
25534      * @param {String} id
25535      * @return {Node}
25536      */
25537     getNodeById : function(id){
25538         return this.nodeHash[id];
25539     },
25540
25541     registerNode : function(node){
25542         this.nodeHash[node.id] = node;
25543     },
25544
25545     unregisterNode : function(node){
25546         delete this.nodeHash[node.id];
25547     },
25548
25549     toString : function(){
25550         return "[Tree"+(this.id?" "+this.id:"")+"]";
25551     }
25552 });
25553
25554 /**
25555  * @class Roo.data.Node
25556  * @extends Roo.util.Observable
25557  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25558  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25559  * @constructor
25560  * @param {Object} attributes The attributes/config for the node
25561  */
25562 Roo.data.Node = function(attributes){
25563     /**
25564      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25565      * @type {Object}
25566      */
25567     this.attributes = attributes || {};
25568     this.leaf = this.attributes.leaf;
25569     /**
25570      * The node id. @type String
25571      */
25572     this.id = this.attributes.id;
25573     if(!this.id){
25574         this.id = Roo.id(null, "ynode-");
25575         this.attributes.id = this.id;
25576     }
25577      
25578     
25579     /**
25580      * All child nodes of this node. @type Array
25581      */
25582     this.childNodes = [];
25583     if(!this.childNodes.indexOf){ // indexOf is a must
25584         this.childNodes.indexOf = function(o){
25585             for(var i = 0, len = this.length; i < len; i++){
25586                 if(this[i] == o) {
25587                     return i;
25588                 }
25589             }
25590             return -1;
25591         };
25592     }
25593     /**
25594      * The parent node for this node. @type Node
25595      */
25596     this.parentNode = null;
25597     /**
25598      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25599      */
25600     this.firstChild = null;
25601     /**
25602      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25603      */
25604     this.lastChild = null;
25605     /**
25606      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25607      */
25608     this.previousSibling = null;
25609     /**
25610      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25611      */
25612     this.nextSibling = null;
25613
25614     this.addEvents({
25615        /**
25616         * @event append
25617         * Fires when a new child node is appended
25618         * @param {Tree} tree The owner tree
25619         * @param {Node} this This node
25620         * @param {Node} node The newly appended node
25621         * @param {Number} index The index of the newly appended node
25622         */
25623        "append" : true,
25624        /**
25625         * @event remove
25626         * Fires when a child node is removed
25627         * @param {Tree} tree The owner tree
25628         * @param {Node} this This node
25629         * @param {Node} node The removed node
25630         */
25631        "remove" : true,
25632        /**
25633         * @event move
25634         * Fires when this node is moved to a new location in the tree
25635         * @param {Tree} tree The owner tree
25636         * @param {Node} this This node
25637         * @param {Node} oldParent The old parent of this node
25638         * @param {Node} newParent The new parent of this node
25639         * @param {Number} index The index it was moved to
25640         */
25641        "move" : true,
25642        /**
25643         * @event insert
25644         * Fires when a new child node is inserted.
25645         * @param {Tree} tree The owner tree
25646         * @param {Node} this This node
25647         * @param {Node} node The child node inserted
25648         * @param {Node} refNode The child node the node was inserted before
25649         */
25650        "insert" : true,
25651        /**
25652         * @event beforeappend
25653         * Fires before a new child is appended, return false to cancel the append.
25654         * @param {Tree} tree The owner tree
25655         * @param {Node} this This node
25656         * @param {Node} node The child node to be appended
25657         */
25658        "beforeappend" : true,
25659        /**
25660         * @event beforeremove
25661         * Fires before a child is removed, return false to cancel the remove.
25662         * @param {Tree} tree The owner tree
25663         * @param {Node} this This node
25664         * @param {Node} node The child node to be removed
25665         */
25666        "beforeremove" : true,
25667        /**
25668         * @event beforemove
25669         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25670         * @param {Tree} tree The owner tree
25671         * @param {Node} this This node
25672         * @param {Node} oldParent The parent of this node
25673         * @param {Node} newParent The new parent this node is moving to
25674         * @param {Number} index The index it is being moved to
25675         */
25676        "beforemove" : true,
25677        /**
25678         * @event beforeinsert
25679         * Fires before a new child is inserted, return false to cancel the insert.
25680         * @param {Tree} tree The owner tree
25681         * @param {Node} this This node
25682         * @param {Node} node The child node to be inserted
25683         * @param {Node} refNode The child node the node is being inserted before
25684         */
25685        "beforeinsert" : true
25686    });
25687     this.listeners = this.attributes.listeners;
25688     Roo.data.Node.superclass.constructor.call(this);
25689 };
25690
25691 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25692     fireEvent : function(evtName){
25693         // first do standard event for this node
25694         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25695             return false;
25696         }
25697         // then bubble it up to the tree if the event wasn't cancelled
25698         var ot = this.getOwnerTree();
25699         if(ot){
25700             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25701                 return false;
25702             }
25703         }
25704         return true;
25705     },
25706
25707     /**
25708      * Returns true if this node is a leaf
25709      * @return {Boolean}
25710      */
25711     isLeaf : function(){
25712         return this.leaf === true;
25713     },
25714
25715     // private
25716     setFirstChild : function(node){
25717         this.firstChild = node;
25718     },
25719
25720     //private
25721     setLastChild : function(node){
25722         this.lastChild = node;
25723     },
25724
25725
25726     /**
25727      * Returns true if this node is the last child of its parent
25728      * @return {Boolean}
25729      */
25730     isLast : function(){
25731        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25732     },
25733
25734     /**
25735      * Returns true if this node is the first child of its parent
25736      * @return {Boolean}
25737      */
25738     isFirst : function(){
25739        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25740     },
25741
25742     hasChildNodes : function(){
25743         return !this.isLeaf() && this.childNodes.length > 0;
25744     },
25745
25746     /**
25747      * Insert node(s) as the last child node of this node.
25748      * @param {Node/Array} node The node or Array of nodes to append
25749      * @return {Node} The appended node if single append, or null if an array was passed
25750      */
25751     appendChild : function(node){
25752         var multi = false;
25753         if(node instanceof Array){
25754             multi = node;
25755         }else if(arguments.length > 1){
25756             multi = arguments;
25757         }
25758         
25759         // if passed an array or multiple args do them one by one
25760         if(multi){
25761             for(var i = 0, len = multi.length; i < len; i++) {
25762                 this.appendChild(multi[i]);
25763             }
25764         }else{
25765             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25766                 return false;
25767             }
25768             var index = this.childNodes.length;
25769             var oldParent = node.parentNode;
25770             // it's a move, make sure we move it cleanly
25771             if(oldParent){
25772                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25773                     return false;
25774                 }
25775                 oldParent.removeChild(node);
25776             }
25777             
25778             index = this.childNodes.length;
25779             if(index == 0){
25780                 this.setFirstChild(node);
25781             }
25782             this.childNodes.push(node);
25783             node.parentNode = this;
25784             var ps = this.childNodes[index-1];
25785             if(ps){
25786                 node.previousSibling = ps;
25787                 ps.nextSibling = node;
25788             }else{
25789                 node.previousSibling = null;
25790             }
25791             node.nextSibling = null;
25792             this.setLastChild(node);
25793             node.setOwnerTree(this.getOwnerTree());
25794             this.fireEvent("append", this.ownerTree, this, node, index);
25795             if(this.ownerTree) {
25796                 this.ownerTree.fireEvent("appendnode", this, node, index);
25797             }
25798             if(oldParent){
25799                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25800             }
25801             return node;
25802         }
25803     },
25804
25805     /**
25806      * Removes a child node from this node.
25807      * @param {Node} node The node to remove
25808      * @return {Node} The removed node
25809      */
25810     removeChild : function(node){
25811         var index = this.childNodes.indexOf(node);
25812         if(index == -1){
25813             return false;
25814         }
25815         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25816             return false;
25817         }
25818
25819         // remove it from childNodes collection
25820         this.childNodes.splice(index, 1);
25821
25822         // update siblings
25823         if(node.previousSibling){
25824             node.previousSibling.nextSibling = node.nextSibling;
25825         }
25826         if(node.nextSibling){
25827             node.nextSibling.previousSibling = node.previousSibling;
25828         }
25829
25830         // update child refs
25831         if(this.firstChild == node){
25832             this.setFirstChild(node.nextSibling);
25833         }
25834         if(this.lastChild == node){
25835             this.setLastChild(node.previousSibling);
25836         }
25837
25838         node.setOwnerTree(null);
25839         // clear any references from the node
25840         node.parentNode = null;
25841         node.previousSibling = null;
25842         node.nextSibling = null;
25843         this.fireEvent("remove", this.ownerTree, this, node);
25844         return node;
25845     },
25846
25847     /**
25848      * Inserts the first node before the second node in this nodes childNodes collection.
25849      * @param {Node} node The node to insert
25850      * @param {Node} refNode The node to insert before (if null the node is appended)
25851      * @return {Node} The inserted node
25852      */
25853     insertBefore : function(node, refNode){
25854         if(!refNode){ // like standard Dom, refNode can be null for append
25855             return this.appendChild(node);
25856         }
25857         // nothing to do
25858         if(node == refNode){
25859             return false;
25860         }
25861
25862         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25863             return false;
25864         }
25865         var index = this.childNodes.indexOf(refNode);
25866         var oldParent = node.parentNode;
25867         var refIndex = index;
25868
25869         // when moving internally, indexes will change after remove
25870         if(oldParent == this && this.childNodes.indexOf(node) < index){
25871             refIndex--;
25872         }
25873
25874         // it's a move, make sure we move it cleanly
25875         if(oldParent){
25876             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25877                 return false;
25878             }
25879             oldParent.removeChild(node);
25880         }
25881         if(refIndex == 0){
25882             this.setFirstChild(node);
25883         }
25884         this.childNodes.splice(refIndex, 0, node);
25885         node.parentNode = this;
25886         var ps = this.childNodes[refIndex-1];
25887         if(ps){
25888             node.previousSibling = ps;
25889             ps.nextSibling = node;
25890         }else{
25891             node.previousSibling = null;
25892         }
25893         node.nextSibling = refNode;
25894         refNode.previousSibling = node;
25895         node.setOwnerTree(this.getOwnerTree());
25896         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25897         if(oldParent){
25898             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25899         }
25900         return node;
25901     },
25902
25903     /**
25904      * Returns the child node at the specified index.
25905      * @param {Number} index
25906      * @return {Node}
25907      */
25908     item : function(index){
25909         return this.childNodes[index];
25910     },
25911
25912     /**
25913      * Replaces one child node in this node with another.
25914      * @param {Node} newChild The replacement node
25915      * @param {Node} oldChild The node to replace
25916      * @return {Node} The replaced node
25917      */
25918     replaceChild : function(newChild, oldChild){
25919         this.insertBefore(newChild, oldChild);
25920         this.removeChild(oldChild);
25921         return oldChild;
25922     },
25923
25924     /**
25925      * Returns the index of a child node
25926      * @param {Node} node
25927      * @return {Number} The index of the node or -1 if it was not found
25928      */
25929     indexOf : function(child){
25930         return this.childNodes.indexOf(child);
25931     },
25932
25933     /**
25934      * Returns the tree this node is in.
25935      * @return {Tree}
25936      */
25937     getOwnerTree : function(){
25938         // if it doesn't have one, look for one
25939         if(!this.ownerTree){
25940             var p = this;
25941             while(p){
25942                 if(p.ownerTree){
25943                     this.ownerTree = p.ownerTree;
25944                     break;
25945                 }
25946                 p = p.parentNode;
25947             }
25948         }
25949         return this.ownerTree;
25950     },
25951
25952     /**
25953      * Returns depth of this node (the root node has a depth of 0)
25954      * @return {Number}
25955      */
25956     getDepth : function(){
25957         var depth = 0;
25958         var p = this;
25959         while(p.parentNode){
25960             ++depth;
25961             p = p.parentNode;
25962         }
25963         return depth;
25964     },
25965
25966     // private
25967     setOwnerTree : function(tree){
25968         // if it's move, we need to update everyone
25969         if(tree != this.ownerTree){
25970             if(this.ownerTree){
25971                 this.ownerTree.unregisterNode(this);
25972             }
25973             this.ownerTree = tree;
25974             var cs = this.childNodes;
25975             for(var i = 0, len = cs.length; i < len; i++) {
25976                 cs[i].setOwnerTree(tree);
25977             }
25978             if(tree){
25979                 tree.registerNode(this);
25980             }
25981         }
25982     },
25983
25984     /**
25985      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25986      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25987      * @return {String} The path
25988      */
25989     getPath : function(attr){
25990         attr = attr || "id";
25991         var p = this.parentNode;
25992         var b = [this.attributes[attr]];
25993         while(p){
25994             b.unshift(p.attributes[attr]);
25995             p = p.parentNode;
25996         }
25997         var sep = this.getOwnerTree().pathSeparator;
25998         return sep + b.join(sep);
25999     },
26000
26001     /**
26002      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26003      * function call will be the scope provided or the current node. The arguments to the function
26004      * will be the args provided or the current node. If the function returns false at any point,
26005      * the bubble is stopped.
26006      * @param {Function} fn The function to call
26007      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26008      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26009      */
26010     bubble : function(fn, scope, args){
26011         var p = this;
26012         while(p){
26013             if(fn.call(scope || p, args || p) === false){
26014                 break;
26015             }
26016             p = p.parentNode;
26017         }
26018     },
26019
26020     /**
26021      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26022      * function call will be the scope provided or the current node. The arguments to the function
26023      * will be the args provided or the current node. If the function returns false at any point,
26024      * the cascade is stopped on that branch.
26025      * @param {Function} fn The function to call
26026      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26027      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26028      */
26029     cascade : function(fn, scope, args){
26030         if(fn.call(scope || this, args || this) !== false){
26031             var cs = this.childNodes;
26032             for(var i = 0, len = cs.length; i < len; i++) {
26033                 cs[i].cascade(fn, scope, args);
26034             }
26035         }
26036     },
26037
26038     /**
26039      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26040      * function call will be the scope provided or the current node. The arguments to the function
26041      * will be the args provided or the current node. If the function returns false at any point,
26042      * the iteration stops.
26043      * @param {Function} fn The function to call
26044      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26045      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26046      */
26047     eachChild : function(fn, scope, args){
26048         var cs = this.childNodes;
26049         for(var i = 0, len = cs.length; i < len; i++) {
26050                 if(fn.call(scope || this, args || cs[i]) === false){
26051                     break;
26052                 }
26053         }
26054     },
26055
26056     /**
26057      * Finds the first child that has the attribute with the specified value.
26058      * @param {String} attribute The attribute name
26059      * @param {Mixed} value The value to search for
26060      * @return {Node} The found child or null if none was found
26061      */
26062     findChild : function(attribute, value){
26063         var cs = this.childNodes;
26064         for(var i = 0, len = cs.length; i < len; i++) {
26065                 if(cs[i].attributes[attribute] == value){
26066                     return cs[i];
26067                 }
26068         }
26069         return null;
26070     },
26071
26072     /**
26073      * Finds the first child by a custom function. The child matches if the function passed
26074      * returns true.
26075      * @param {Function} fn
26076      * @param {Object} scope (optional)
26077      * @return {Node} The found child or null if none was found
26078      */
26079     findChildBy : function(fn, scope){
26080         var cs = this.childNodes;
26081         for(var i = 0, len = cs.length; i < len; i++) {
26082                 if(fn.call(scope||cs[i], cs[i]) === true){
26083                     return cs[i];
26084                 }
26085         }
26086         return null;
26087     },
26088
26089     /**
26090      * Sorts this nodes children using the supplied sort function
26091      * @param {Function} fn
26092      * @param {Object} scope (optional)
26093      */
26094     sort : function(fn, scope){
26095         var cs = this.childNodes;
26096         var len = cs.length;
26097         if(len > 0){
26098             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26099             cs.sort(sortFn);
26100             for(var i = 0; i < len; i++){
26101                 var n = cs[i];
26102                 n.previousSibling = cs[i-1];
26103                 n.nextSibling = cs[i+1];
26104                 if(i == 0){
26105                     this.setFirstChild(n);
26106                 }
26107                 if(i == len-1){
26108                     this.setLastChild(n);
26109                 }
26110             }
26111         }
26112     },
26113
26114     /**
26115      * Returns true if this node is an ancestor (at any point) of the passed node.
26116      * @param {Node} node
26117      * @return {Boolean}
26118      */
26119     contains : function(node){
26120         return node.isAncestor(this);
26121     },
26122
26123     /**
26124      * Returns true if the passed node is an ancestor (at any point) of this node.
26125      * @param {Node} node
26126      * @return {Boolean}
26127      */
26128     isAncestor : function(node){
26129         var p = this.parentNode;
26130         while(p){
26131             if(p == node){
26132                 return true;
26133             }
26134             p = p.parentNode;
26135         }
26136         return false;
26137     },
26138
26139     toString : function(){
26140         return "[Node"+(this.id?" "+this.id:"")+"]";
26141     }
26142 });/*
26143  * Based on:
26144  * Ext JS Library 1.1.1
26145  * Copyright(c) 2006-2007, Ext JS, LLC.
26146  *
26147  * Originally Released Under LGPL - original licence link has changed is not relivant.
26148  *
26149  * Fork - LGPL
26150  * <script type="text/javascript">
26151  */
26152
26153
26154 /**
26155  * @class Roo.Shadow
26156  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26157  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26158  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26159  * @constructor
26160  * Create a new Shadow
26161  * @param {Object} config The config object
26162  */
26163 Roo.Shadow = function(config){
26164     Roo.apply(this, config);
26165     if(typeof this.mode != "string"){
26166         this.mode = this.defaultMode;
26167     }
26168     var o = this.offset, a = {h: 0};
26169     var rad = Math.floor(this.offset/2);
26170     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26171         case "drop":
26172             a.w = 0;
26173             a.l = a.t = o;
26174             a.t -= 1;
26175             if(Roo.isIE){
26176                 a.l -= this.offset + rad;
26177                 a.t -= this.offset + rad;
26178                 a.w -= rad;
26179                 a.h -= rad;
26180                 a.t += 1;
26181             }
26182         break;
26183         case "sides":
26184             a.w = (o*2);
26185             a.l = -o;
26186             a.t = o-1;
26187             if(Roo.isIE){
26188                 a.l -= (this.offset - rad);
26189                 a.t -= this.offset + rad;
26190                 a.l += 1;
26191                 a.w -= (this.offset - rad)*2;
26192                 a.w -= rad + 1;
26193                 a.h -= 1;
26194             }
26195         break;
26196         case "frame":
26197             a.w = a.h = (o*2);
26198             a.l = a.t = -o;
26199             a.t += 1;
26200             a.h -= 2;
26201             if(Roo.isIE){
26202                 a.l -= (this.offset - rad);
26203                 a.t -= (this.offset - rad);
26204                 a.l += 1;
26205                 a.w -= (this.offset + rad + 1);
26206                 a.h -= (this.offset + rad);
26207                 a.h += 1;
26208             }
26209         break;
26210     };
26211
26212     this.adjusts = a;
26213 };
26214
26215 Roo.Shadow.prototype = {
26216     /**
26217      * @cfg {String} mode
26218      * The shadow display mode.  Supports the following options:<br />
26219      * sides: Shadow displays on both sides and bottom only<br />
26220      * frame: Shadow displays equally on all four sides<br />
26221      * drop: Traditional bottom-right drop shadow (default)
26222      */
26223     mode: false,
26224     /**
26225      * @cfg {String} offset
26226      * The number of pixels to offset the shadow from the element (defaults to 4)
26227      */
26228     offset: 4,
26229
26230     // private
26231     defaultMode: "drop",
26232
26233     /**
26234      * Displays the shadow under the target element
26235      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26236      */
26237     show : function(target){
26238         target = Roo.get(target);
26239         if(!this.el){
26240             this.el = Roo.Shadow.Pool.pull();
26241             if(this.el.dom.nextSibling != target.dom){
26242                 this.el.insertBefore(target);
26243             }
26244         }
26245         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26246         if(Roo.isIE){
26247             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26248         }
26249         this.realign(
26250             target.getLeft(true),
26251             target.getTop(true),
26252             target.getWidth(),
26253             target.getHeight()
26254         );
26255         this.el.dom.style.display = "block";
26256     },
26257
26258     /**
26259      * Returns true if the shadow is visible, else false
26260      */
26261     isVisible : function(){
26262         return this.el ? true : false;  
26263     },
26264
26265     /**
26266      * Direct alignment when values are already available. Show must be called at least once before
26267      * calling this method to ensure it is initialized.
26268      * @param {Number} left The target element left position
26269      * @param {Number} top The target element top position
26270      * @param {Number} width The target element width
26271      * @param {Number} height The target element height
26272      */
26273     realign : function(l, t, w, h){
26274         if(!this.el){
26275             return;
26276         }
26277         var a = this.adjusts, d = this.el.dom, s = d.style;
26278         var iea = 0;
26279         s.left = (l+a.l)+"px";
26280         s.top = (t+a.t)+"px";
26281         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26282  
26283         if(s.width != sws || s.height != shs){
26284             s.width = sws;
26285             s.height = shs;
26286             if(!Roo.isIE){
26287                 var cn = d.childNodes;
26288                 var sww = Math.max(0, (sw-12))+"px";
26289                 cn[0].childNodes[1].style.width = sww;
26290                 cn[1].childNodes[1].style.width = sww;
26291                 cn[2].childNodes[1].style.width = sww;
26292                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26293             }
26294         }
26295     },
26296
26297     /**
26298      * Hides this shadow
26299      */
26300     hide : function(){
26301         if(this.el){
26302             this.el.dom.style.display = "none";
26303             Roo.Shadow.Pool.push(this.el);
26304             delete this.el;
26305         }
26306     },
26307
26308     /**
26309      * Adjust the z-index of this shadow
26310      * @param {Number} zindex The new z-index
26311      */
26312     setZIndex : function(z){
26313         this.zIndex = z;
26314         if(this.el){
26315             this.el.setStyle("z-index", z);
26316         }
26317     }
26318 };
26319
26320 // Private utility class that manages the internal Shadow cache
26321 Roo.Shadow.Pool = function(){
26322     var p = [];
26323     var markup = Roo.isIE ?
26324                  '<div class="x-ie-shadow"></div>' :
26325                  '<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>';
26326     return {
26327         pull : function(){
26328             var sh = p.shift();
26329             if(!sh){
26330                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26331                 sh.autoBoxAdjust = false;
26332             }
26333             return sh;
26334         },
26335
26336         push : function(sh){
26337             p.push(sh);
26338         }
26339     };
26340 }();/*
26341  * Based on:
26342  * Ext JS Library 1.1.1
26343  * Copyright(c) 2006-2007, Ext JS, LLC.
26344  *
26345  * Originally Released Under LGPL - original licence link has changed is not relivant.
26346  *
26347  * Fork - LGPL
26348  * <script type="text/javascript">
26349  */
26350
26351
26352 /**
26353  * @class Roo.SplitBar
26354  * @extends Roo.util.Observable
26355  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26356  * <br><br>
26357  * Usage:
26358  * <pre><code>
26359 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26360                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26361 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26362 split.minSize = 100;
26363 split.maxSize = 600;
26364 split.animate = true;
26365 split.on('moved', splitterMoved);
26366 </code></pre>
26367  * @constructor
26368  * Create a new SplitBar
26369  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26370  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26371  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26372  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26373                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26374                         position of the SplitBar).
26375  */
26376 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26377     
26378     /** @private */
26379     this.el = Roo.get(dragElement, true);
26380     this.el.dom.unselectable = "on";
26381     /** @private */
26382     this.resizingEl = Roo.get(resizingElement, true);
26383
26384     /**
26385      * @private
26386      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26387      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26388      * @type Number
26389      */
26390     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26391     
26392     /**
26393      * The minimum size of the resizing element. (Defaults to 0)
26394      * @type Number
26395      */
26396     this.minSize = 0;
26397     
26398     /**
26399      * The maximum size of the resizing element. (Defaults to 2000)
26400      * @type Number
26401      */
26402     this.maxSize = 2000;
26403     
26404     /**
26405      * Whether to animate the transition to the new size
26406      * @type Boolean
26407      */
26408     this.animate = false;
26409     
26410     /**
26411      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26412      * @type Boolean
26413      */
26414     this.useShim = false;
26415     
26416     /** @private */
26417     this.shim = null;
26418     
26419     if(!existingProxy){
26420         /** @private */
26421         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26422     }else{
26423         this.proxy = Roo.get(existingProxy).dom;
26424     }
26425     /** @private */
26426     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26427     
26428     /** @private */
26429     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26430     
26431     /** @private */
26432     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26433     
26434     /** @private */
26435     this.dragSpecs = {};
26436     
26437     /**
26438      * @private The adapter to use to positon and resize elements
26439      */
26440     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26441     this.adapter.init(this);
26442     
26443     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26444         /** @private */
26445         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26446         this.el.addClass("x-splitbar-h");
26447     }else{
26448         /** @private */
26449         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26450         this.el.addClass("x-splitbar-v");
26451     }
26452     
26453     this.addEvents({
26454         /**
26455          * @event resize
26456          * Fires when the splitter is moved (alias for {@link #event-moved})
26457          * @param {Roo.SplitBar} this
26458          * @param {Number} newSize the new width or height
26459          */
26460         "resize" : true,
26461         /**
26462          * @event moved
26463          * Fires when the splitter is moved
26464          * @param {Roo.SplitBar} this
26465          * @param {Number} newSize the new width or height
26466          */
26467         "moved" : true,
26468         /**
26469          * @event beforeresize
26470          * Fires before the splitter is dragged
26471          * @param {Roo.SplitBar} this
26472          */
26473         "beforeresize" : true,
26474
26475         "beforeapply" : true
26476     });
26477
26478     Roo.util.Observable.call(this);
26479 };
26480
26481 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26482     onStartProxyDrag : function(x, y){
26483         this.fireEvent("beforeresize", this);
26484         if(!this.overlay){
26485             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26486             o.unselectable();
26487             o.enableDisplayMode("block");
26488             // all splitbars share the same overlay
26489             Roo.SplitBar.prototype.overlay = o;
26490         }
26491         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26492         this.overlay.show();
26493         Roo.get(this.proxy).setDisplayed("block");
26494         var size = this.adapter.getElementSize(this);
26495         this.activeMinSize = this.getMinimumSize();;
26496         this.activeMaxSize = this.getMaximumSize();;
26497         var c1 = size - this.activeMinSize;
26498         var c2 = Math.max(this.activeMaxSize - size, 0);
26499         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26500             this.dd.resetConstraints();
26501             this.dd.setXConstraint(
26502                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26503                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26504             );
26505             this.dd.setYConstraint(0, 0);
26506         }else{
26507             this.dd.resetConstraints();
26508             this.dd.setXConstraint(0, 0);
26509             this.dd.setYConstraint(
26510                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26511                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26512             );
26513          }
26514         this.dragSpecs.startSize = size;
26515         this.dragSpecs.startPoint = [x, y];
26516         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26517     },
26518     
26519     /** 
26520      * @private Called after the drag operation by the DDProxy
26521      */
26522     onEndProxyDrag : function(e){
26523         Roo.get(this.proxy).setDisplayed(false);
26524         var endPoint = Roo.lib.Event.getXY(e);
26525         if(this.overlay){
26526             this.overlay.hide();
26527         }
26528         var newSize;
26529         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26530             newSize = this.dragSpecs.startSize + 
26531                 (this.placement == Roo.SplitBar.LEFT ?
26532                     endPoint[0] - this.dragSpecs.startPoint[0] :
26533                     this.dragSpecs.startPoint[0] - endPoint[0]
26534                 );
26535         }else{
26536             newSize = this.dragSpecs.startSize + 
26537                 (this.placement == Roo.SplitBar.TOP ?
26538                     endPoint[1] - this.dragSpecs.startPoint[1] :
26539                     this.dragSpecs.startPoint[1] - endPoint[1]
26540                 );
26541         }
26542         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26543         if(newSize != this.dragSpecs.startSize){
26544             if(this.fireEvent('beforeapply', this, newSize) !== false){
26545                 this.adapter.setElementSize(this, newSize);
26546                 this.fireEvent("moved", this, newSize);
26547                 this.fireEvent("resize", this, newSize);
26548             }
26549         }
26550     },
26551     
26552     /**
26553      * Get the adapter this SplitBar uses
26554      * @return The adapter object
26555      */
26556     getAdapter : function(){
26557         return this.adapter;
26558     },
26559     
26560     /**
26561      * Set the adapter this SplitBar uses
26562      * @param {Object} adapter A SplitBar adapter object
26563      */
26564     setAdapter : function(adapter){
26565         this.adapter = adapter;
26566         this.adapter.init(this);
26567     },
26568     
26569     /**
26570      * Gets the minimum size for the resizing element
26571      * @return {Number} The minimum size
26572      */
26573     getMinimumSize : function(){
26574         return this.minSize;
26575     },
26576     
26577     /**
26578      * Sets the minimum size for the resizing element
26579      * @param {Number} minSize The minimum size
26580      */
26581     setMinimumSize : function(minSize){
26582         this.minSize = minSize;
26583     },
26584     
26585     /**
26586      * Gets the maximum size for the resizing element
26587      * @return {Number} The maximum size
26588      */
26589     getMaximumSize : function(){
26590         return this.maxSize;
26591     },
26592     
26593     /**
26594      * Sets the maximum size for the resizing element
26595      * @param {Number} maxSize The maximum size
26596      */
26597     setMaximumSize : function(maxSize){
26598         this.maxSize = maxSize;
26599     },
26600     
26601     /**
26602      * Sets the initialize size for the resizing element
26603      * @param {Number} size The initial size
26604      */
26605     setCurrentSize : function(size){
26606         var oldAnimate = this.animate;
26607         this.animate = false;
26608         this.adapter.setElementSize(this, size);
26609         this.animate = oldAnimate;
26610     },
26611     
26612     /**
26613      * Destroy this splitbar. 
26614      * @param {Boolean} removeEl True to remove the element
26615      */
26616     destroy : function(removeEl){
26617         if(this.shim){
26618             this.shim.remove();
26619         }
26620         this.dd.unreg();
26621         this.proxy.parentNode.removeChild(this.proxy);
26622         if(removeEl){
26623             this.el.remove();
26624         }
26625     }
26626 });
26627
26628 /**
26629  * @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.
26630  */
26631 Roo.SplitBar.createProxy = function(dir){
26632     var proxy = new Roo.Element(document.createElement("div"));
26633     proxy.unselectable();
26634     var cls = 'x-splitbar-proxy';
26635     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26636     document.body.appendChild(proxy.dom);
26637     return proxy.dom;
26638 };
26639
26640 /** 
26641  * @class Roo.SplitBar.BasicLayoutAdapter
26642  * Default Adapter. It assumes the splitter and resizing element are not positioned
26643  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26644  */
26645 Roo.SplitBar.BasicLayoutAdapter = function(){
26646 };
26647
26648 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26649     // do nothing for now
26650     init : function(s){
26651     
26652     },
26653     /**
26654      * Called before drag operations to get the current size of the resizing element. 
26655      * @param {Roo.SplitBar} s The SplitBar using this adapter
26656      */
26657      getElementSize : function(s){
26658         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26659             return s.resizingEl.getWidth();
26660         }else{
26661             return s.resizingEl.getHeight();
26662         }
26663     },
26664     
26665     /**
26666      * Called after drag operations to set the size of the resizing element.
26667      * @param {Roo.SplitBar} s The SplitBar using this adapter
26668      * @param {Number} newSize The new size to set
26669      * @param {Function} onComplete A function to be invoked when resizing is complete
26670      */
26671     setElementSize : function(s, newSize, onComplete){
26672         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26673             if(!s.animate){
26674                 s.resizingEl.setWidth(newSize);
26675                 if(onComplete){
26676                     onComplete(s, newSize);
26677                 }
26678             }else{
26679                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26680             }
26681         }else{
26682             
26683             if(!s.animate){
26684                 s.resizingEl.setHeight(newSize);
26685                 if(onComplete){
26686                     onComplete(s, newSize);
26687                 }
26688             }else{
26689                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26690             }
26691         }
26692     }
26693 };
26694
26695 /** 
26696  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26697  * @extends Roo.SplitBar.BasicLayoutAdapter
26698  * Adapter that  moves the splitter element to align with the resized sizing element. 
26699  * Used with an absolute positioned SplitBar.
26700  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26701  * document.body, make sure you assign an id to the body element.
26702  */
26703 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26704     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26705     this.container = Roo.get(container);
26706 };
26707
26708 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26709     init : function(s){
26710         this.basic.init(s);
26711     },
26712     
26713     getElementSize : function(s){
26714         return this.basic.getElementSize(s);
26715     },
26716     
26717     setElementSize : function(s, newSize, onComplete){
26718         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26719     },
26720     
26721     moveSplitter : function(s){
26722         var yes = Roo.SplitBar;
26723         switch(s.placement){
26724             case yes.LEFT:
26725                 s.el.setX(s.resizingEl.getRight());
26726                 break;
26727             case yes.RIGHT:
26728                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26729                 break;
26730             case yes.TOP:
26731                 s.el.setY(s.resizingEl.getBottom());
26732                 break;
26733             case yes.BOTTOM:
26734                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26735                 break;
26736         }
26737     }
26738 };
26739
26740 /**
26741  * Orientation constant - Create a vertical SplitBar
26742  * @static
26743  * @type Number
26744  */
26745 Roo.SplitBar.VERTICAL = 1;
26746
26747 /**
26748  * Orientation constant - Create a horizontal SplitBar
26749  * @static
26750  * @type Number
26751  */
26752 Roo.SplitBar.HORIZONTAL = 2;
26753
26754 /**
26755  * Placement constant - The resizing element is to the left of the splitter element
26756  * @static
26757  * @type Number
26758  */
26759 Roo.SplitBar.LEFT = 1;
26760
26761 /**
26762  * Placement constant - The resizing element is to the right of the splitter element
26763  * @static
26764  * @type Number
26765  */
26766 Roo.SplitBar.RIGHT = 2;
26767
26768 /**
26769  * Placement constant - The resizing element is positioned above the splitter element
26770  * @static
26771  * @type Number
26772  */
26773 Roo.SplitBar.TOP = 3;
26774
26775 /**
26776  * Placement constant - The resizing element is positioned under splitter element
26777  * @static
26778  * @type Number
26779  */
26780 Roo.SplitBar.BOTTOM = 4;
26781 /*
26782  * Based on:
26783  * Ext JS Library 1.1.1
26784  * Copyright(c) 2006-2007, Ext JS, LLC.
26785  *
26786  * Originally Released Under LGPL - original licence link has changed is not relivant.
26787  *
26788  * Fork - LGPL
26789  * <script type="text/javascript">
26790  */
26791
26792 /**
26793  * @class Roo.View
26794  * @extends Roo.util.Observable
26795  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26796  * This class also supports single and multi selection modes. <br>
26797  * Create a data model bound view:
26798  <pre><code>
26799  var store = new Roo.data.Store(...);
26800
26801  var view = new Roo.View({
26802     el : "my-element",
26803     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26804  
26805     singleSelect: true,
26806     selectedClass: "ydataview-selected",
26807     store: store
26808  });
26809
26810  // listen for node click?
26811  view.on("click", function(vw, index, node, e){
26812  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26813  });
26814
26815  // load XML data
26816  dataModel.load("foobar.xml");
26817  </code></pre>
26818  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26819  * <br><br>
26820  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26821  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26822  * 
26823  * Note: old style constructor is still suported (container, template, config)
26824  * 
26825  * @constructor
26826  * Create a new View
26827  * @param {Object} config The config object
26828  * 
26829  */
26830 Roo.View = function(config, depreciated_tpl, depreciated_config){
26831     
26832     this.parent = false;
26833     
26834     if (typeof(depreciated_tpl) == 'undefined') {
26835         // new way.. - universal constructor.
26836         Roo.apply(this, config);
26837         this.el  = Roo.get(this.el);
26838     } else {
26839         // old format..
26840         this.el  = Roo.get(config);
26841         this.tpl = depreciated_tpl;
26842         Roo.apply(this, depreciated_config);
26843     }
26844     this.wrapEl  = this.el.wrap().wrap();
26845     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26846     
26847     
26848     if(typeof(this.tpl) == "string"){
26849         this.tpl = new Roo.Template(this.tpl);
26850     } else {
26851         // support xtype ctors..
26852         this.tpl = new Roo.factory(this.tpl, Roo);
26853     }
26854     
26855     
26856     this.tpl.compile();
26857     
26858     /** @private */
26859     this.addEvents({
26860         /**
26861          * @event beforeclick
26862          * Fires before a click is processed. Returns false to cancel the default action.
26863          * @param {Roo.View} this
26864          * @param {Number} index The index of the target node
26865          * @param {HTMLElement} node The target node
26866          * @param {Roo.EventObject} e The raw event object
26867          */
26868             "beforeclick" : true,
26869         /**
26870          * @event click
26871          * Fires when a template node is clicked.
26872          * @param {Roo.View} this
26873          * @param {Number} index The index of the target node
26874          * @param {HTMLElement} node The target node
26875          * @param {Roo.EventObject} e The raw event object
26876          */
26877             "click" : true,
26878         /**
26879          * @event dblclick
26880          * Fires when a template node is double clicked.
26881          * @param {Roo.View} this
26882          * @param {Number} index The index of the target node
26883          * @param {HTMLElement} node The target node
26884          * @param {Roo.EventObject} e The raw event object
26885          */
26886             "dblclick" : true,
26887         /**
26888          * @event contextmenu
26889          * Fires when a template node is right clicked.
26890          * @param {Roo.View} this
26891          * @param {Number} index The index of the target node
26892          * @param {HTMLElement} node The target node
26893          * @param {Roo.EventObject} e The raw event object
26894          */
26895             "contextmenu" : true,
26896         /**
26897          * @event selectionchange
26898          * Fires when the selected nodes change.
26899          * @param {Roo.View} this
26900          * @param {Array} selections Array of the selected nodes
26901          */
26902             "selectionchange" : true,
26903     
26904         /**
26905          * @event beforeselect
26906          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26907          * @param {Roo.View} this
26908          * @param {HTMLElement} node The node to be selected
26909          * @param {Array} selections Array of currently selected nodes
26910          */
26911             "beforeselect" : true,
26912         /**
26913          * @event preparedata
26914          * Fires on every row to render, to allow you to change the data.
26915          * @param {Roo.View} this
26916          * @param {Object} data to be rendered (change this)
26917          */
26918           "preparedata" : true
26919           
26920           
26921         });
26922
26923
26924
26925     this.el.on({
26926         "click": this.onClick,
26927         "dblclick": this.onDblClick,
26928         "contextmenu": this.onContextMenu,
26929         scope:this
26930     });
26931
26932     this.selections = [];
26933     this.nodes = [];
26934     this.cmp = new Roo.CompositeElementLite([]);
26935     if(this.store){
26936         this.store = Roo.factory(this.store, Roo.data);
26937         this.setStore(this.store, true);
26938     }
26939     
26940     if ( this.footer && this.footer.xtype) {
26941            
26942          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26943         
26944         this.footer.dataSource = this.store;
26945         this.footer.container = fctr;
26946         this.footer = Roo.factory(this.footer, Roo);
26947         fctr.insertFirst(this.el);
26948         
26949         // this is a bit insane - as the paging toolbar seems to detach the el..
26950 //        dom.parentNode.parentNode.parentNode
26951          // they get detached?
26952     }
26953     
26954     
26955     Roo.View.superclass.constructor.call(this);
26956     
26957     
26958 };
26959
26960 Roo.extend(Roo.View, Roo.util.Observable, {
26961     
26962      /**
26963      * @cfg {Roo.data.Store} store Data store to load data from.
26964      */
26965     store : false,
26966     
26967     /**
26968      * @cfg {String|Roo.Element} el The container element.
26969      */
26970     el : '',
26971     
26972     /**
26973      * @cfg {String|Roo.Template} tpl The template used by this View 
26974      */
26975     tpl : false,
26976     /**
26977      * @cfg {String} dataName the named area of the template to use as the data area
26978      *                          Works with domtemplates roo-name="name"
26979      */
26980     dataName: false,
26981     /**
26982      * @cfg {String} selectedClass The css class to add to selected nodes
26983      */
26984     selectedClass : "x-view-selected",
26985      /**
26986      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26987      */
26988     emptyText : "",
26989     
26990     /**
26991      * @cfg {String} text to display on mask (default Loading)
26992      */
26993     mask : false,
26994     /**
26995      * @cfg {Boolean} multiSelect Allow multiple selection
26996      */
26997     multiSelect : false,
26998     /**
26999      * @cfg {Boolean} singleSelect Allow single selection
27000      */
27001     singleSelect:  false,
27002     
27003     /**
27004      * @cfg {Boolean} toggleSelect - selecting 
27005      */
27006     toggleSelect : false,
27007     
27008     /**
27009      * @cfg {Boolean} tickable - selecting 
27010      */
27011     tickable : false,
27012     
27013     /**
27014      * Returns the element this view is bound to.
27015      * @return {Roo.Element}
27016      */
27017     getEl : function(){
27018         return this.wrapEl;
27019     },
27020     
27021     
27022
27023     /**
27024      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27025      */
27026     refresh : function(){
27027         //Roo.log('refresh');
27028         var t = this.tpl;
27029         
27030         // if we are using something like 'domtemplate', then
27031         // the what gets used is:
27032         // t.applySubtemplate(NAME, data, wrapping data..)
27033         // the outer template then get' applied with
27034         //     the store 'extra data'
27035         // and the body get's added to the
27036         //      roo-name="data" node?
27037         //      <span class='roo-tpl-{name}'></span> ?????
27038         
27039         
27040         
27041         this.clearSelections();
27042         this.el.update("");
27043         var html = [];
27044         var records = this.store.getRange();
27045         if(records.length < 1) {
27046             
27047             // is this valid??  = should it render a template??
27048             
27049             this.el.update(this.emptyText);
27050             return;
27051         }
27052         var el = this.el;
27053         if (this.dataName) {
27054             this.el.update(t.apply(this.store.meta)); //????
27055             el = this.el.child('.roo-tpl-' + this.dataName);
27056         }
27057         
27058         for(var i = 0, len = records.length; i < len; i++){
27059             var data = this.prepareData(records[i].data, i, records[i]);
27060             this.fireEvent("preparedata", this, data, i, records[i]);
27061             
27062             var d = Roo.apply({}, data);
27063             
27064             if(this.tickable){
27065                 Roo.apply(d, {'roo-id' : Roo.id()});
27066                 
27067                 var _this = this;
27068             
27069                 Roo.each(this.parent.item, function(item){
27070                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27071                         return;
27072                     }
27073                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27074                 });
27075             }
27076             
27077             html[html.length] = Roo.util.Format.trim(
27078                 this.dataName ?
27079                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27080                     t.apply(d)
27081             );
27082         }
27083         
27084         
27085         
27086         el.update(html.join(""));
27087         this.nodes = el.dom.childNodes;
27088         this.updateIndexes(0);
27089     },
27090     
27091
27092     /**
27093      * Function to override to reformat the data that is sent to
27094      * the template for each node.
27095      * DEPRICATED - use the preparedata event handler.
27096      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27097      * a JSON object for an UpdateManager bound view).
27098      */
27099     prepareData : function(data, index, record)
27100     {
27101         this.fireEvent("preparedata", this, data, index, record);
27102         return data;
27103     },
27104
27105     onUpdate : function(ds, record){
27106         // Roo.log('on update');   
27107         this.clearSelections();
27108         var index = this.store.indexOf(record);
27109         var n = this.nodes[index];
27110         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27111         n.parentNode.removeChild(n);
27112         this.updateIndexes(index, index);
27113     },
27114
27115     
27116     
27117 // --------- FIXME     
27118     onAdd : function(ds, records, index)
27119     {
27120         //Roo.log(['on Add', ds, records, index] );        
27121         this.clearSelections();
27122         if(this.nodes.length == 0){
27123             this.refresh();
27124             return;
27125         }
27126         var n = this.nodes[index];
27127         for(var i = 0, len = records.length; i < len; i++){
27128             var d = this.prepareData(records[i].data, i, records[i]);
27129             if(n){
27130                 this.tpl.insertBefore(n, d);
27131             }else{
27132                 
27133                 this.tpl.append(this.el, d);
27134             }
27135         }
27136         this.updateIndexes(index);
27137     },
27138
27139     onRemove : function(ds, record, index){
27140        // Roo.log('onRemove');
27141         this.clearSelections();
27142         var el = this.dataName  ?
27143             this.el.child('.roo-tpl-' + this.dataName) :
27144             this.el; 
27145         
27146         el.dom.removeChild(this.nodes[index]);
27147         this.updateIndexes(index);
27148     },
27149
27150     /**
27151      * Refresh an individual node.
27152      * @param {Number} index
27153      */
27154     refreshNode : function(index){
27155         this.onUpdate(this.store, this.store.getAt(index));
27156     },
27157
27158     updateIndexes : function(startIndex, endIndex){
27159         var ns = this.nodes;
27160         startIndex = startIndex || 0;
27161         endIndex = endIndex || ns.length - 1;
27162         for(var i = startIndex; i <= endIndex; i++){
27163             ns[i].nodeIndex = i;
27164         }
27165     },
27166
27167     /**
27168      * Changes the data store this view uses and refresh the view.
27169      * @param {Store} store
27170      */
27171     setStore : function(store, initial){
27172         if(!initial && this.store){
27173             this.store.un("datachanged", this.refresh);
27174             this.store.un("add", this.onAdd);
27175             this.store.un("remove", this.onRemove);
27176             this.store.un("update", this.onUpdate);
27177             this.store.un("clear", this.refresh);
27178             this.store.un("beforeload", this.onBeforeLoad);
27179             this.store.un("load", this.onLoad);
27180             this.store.un("loadexception", this.onLoad);
27181         }
27182         if(store){
27183           
27184             store.on("datachanged", this.refresh, this);
27185             store.on("add", this.onAdd, this);
27186             store.on("remove", this.onRemove, this);
27187             store.on("update", this.onUpdate, this);
27188             store.on("clear", this.refresh, this);
27189             store.on("beforeload", this.onBeforeLoad, this);
27190             store.on("load", this.onLoad, this);
27191             store.on("loadexception", this.onLoad, this);
27192         }
27193         
27194         if(store){
27195             this.refresh();
27196         }
27197     },
27198     /**
27199      * onbeforeLoad - masks the loading area.
27200      *
27201      */
27202     onBeforeLoad : function(store,opts)
27203     {
27204          //Roo.log('onBeforeLoad');   
27205         if (!opts.add) {
27206             this.el.update("");
27207         }
27208         this.el.mask(this.mask ? this.mask : "Loading" ); 
27209     },
27210     onLoad : function ()
27211     {
27212         this.el.unmask();
27213     },
27214     
27215
27216     /**
27217      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27218      * @param {HTMLElement} node
27219      * @return {HTMLElement} The template node
27220      */
27221     findItemFromChild : function(node){
27222         var el = this.dataName  ?
27223             this.el.child('.roo-tpl-' + this.dataName,true) :
27224             this.el.dom; 
27225         
27226         if(!node || node.parentNode == el){
27227                     return node;
27228             }
27229             var p = node.parentNode;
27230             while(p && p != el){
27231             if(p.parentNode == el){
27232                 return p;
27233             }
27234             p = p.parentNode;
27235         }
27236             return null;
27237     },
27238
27239     /** @ignore */
27240     onClick : function(e){
27241         var item = this.findItemFromChild(e.getTarget());
27242         if(item){
27243             var index = this.indexOf(item);
27244             if(this.onItemClick(item, index, e) !== false){
27245                 this.fireEvent("click", this, index, item, e);
27246             }
27247         }else{
27248             this.clearSelections();
27249         }
27250     },
27251
27252     /** @ignore */
27253     onContextMenu : function(e){
27254         var item = this.findItemFromChild(e.getTarget());
27255         if(item){
27256             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27257         }
27258     },
27259
27260     /** @ignore */
27261     onDblClick : function(e){
27262         var item = this.findItemFromChild(e.getTarget());
27263         if(item){
27264             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27265         }
27266     },
27267
27268     onItemClick : function(item, index, e)
27269     {
27270         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27271             return false;
27272         }
27273         if (this.toggleSelect) {
27274             var m = this.isSelected(item) ? 'unselect' : 'select';
27275             //Roo.log(m);
27276             var _t = this;
27277             _t[m](item, true, false);
27278             return true;
27279         }
27280         if(this.multiSelect || this.singleSelect){
27281             if(this.multiSelect && e.shiftKey && this.lastSelection){
27282                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27283             }else{
27284                 this.select(item, this.multiSelect && e.ctrlKey);
27285                 this.lastSelection = item;
27286             }
27287             
27288             if(!this.tickable){
27289                 e.preventDefault();
27290             }
27291             
27292         }
27293         return true;
27294     },
27295
27296     /**
27297      * Get the number of selected nodes.
27298      * @return {Number}
27299      */
27300     getSelectionCount : function(){
27301         return this.selections.length;
27302     },
27303
27304     /**
27305      * Get the currently selected nodes.
27306      * @return {Array} An array of HTMLElements
27307      */
27308     getSelectedNodes : function(){
27309         return this.selections;
27310     },
27311
27312     /**
27313      * Get the indexes of the selected nodes.
27314      * @return {Array}
27315      */
27316     getSelectedIndexes : function(){
27317         var indexes = [], s = this.selections;
27318         for(var i = 0, len = s.length; i < len; i++){
27319             indexes.push(s[i].nodeIndex);
27320         }
27321         return indexes;
27322     },
27323
27324     /**
27325      * Clear all selections
27326      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27327      */
27328     clearSelections : function(suppressEvent){
27329         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27330             this.cmp.elements = this.selections;
27331             this.cmp.removeClass(this.selectedClass);
27332             this.selections = [];
27333             if(!suppressEvent){
27334                 this.fireEvent("selectionchange", this, this.selections);
27335             }
27336         }
27337     },
27338
27339     /**
27340      * Returns true if the passed node is selected
27341      * @param {HTMLElement/Number} node The node or node index
27342      * @return {Boolean}
27343      */
27344     isSelected : function(node){
27345         var s = this.selections;
27346         if(s.length < 1){
27347             return false;
27348         }
27349         node = this.getNode(node);
27350         return s.indexOf(node) !== -1;
27351     },
27352
27353     /**
27354      * Selects nodes.
27355      * @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
27356      * @param {Boolean} keepExisting (optional) true to keep existing selections
27357      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27358      */
27359     select : function(nodeInfo, keepExisting, suppressEvent){
27360         if(nodeInfo instanceof Array){
27361             if(!keepExisting){
27362                 this.clearSelections(true);
27363             }
27364             for(var i = 0, len = nodeInfo.length; i < len; i++){
27365                 this.select(nodeInfo[i], true, true);
27366             }
27367             return;
27368         } 
27369         var node = this.getNode(nodeInfo);
27370         if(!node || this.isSelected(node)){
27371             return; // already selected.
27372         }
27373         if(!keepExisting){
27374             this.clearSelections(true);
27375         }
27376         
27377         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27378             Roo.fly(node).addClass(this.selectedClass);
27379             this.selections.push(node);
27380             if(!suppressEvent){
27381                 this.fireEvent("selectionchange", this, this.selections);
27382             }
27383         }
27384         
27385         
27386     },
27387       /**
27388      * Unselects nodes.
27389      * @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
27390      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27391      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27392      */
27393     unselect : function(nodeInfo, keepExisting, suppressEvent)
27394     {
27395         if(nodeInfo instanceof Array){
27396             Roo.each(this.selections, function(s) {
27397                 this.unselect(s, nodeInfo);
27398             }, this);
27399             return;
27400         }
27401         var node = this.getNode(nodeInfo);
27402         if(!node || !this.isSelected(node)){
27403             //Roo.log("not selected");
27404             return; // not selected.
27405         }
27406         // fireevent???
27407         var ns = [];
27408         Roo.each(this.selections, function(s) {
27409             if (s == node ) {
27410                 Roo.fly(node).removeClass(this.selectedClass);
27411
27412                 return;
27413             }
27414             ns.push(s);
27415         },this);
27416         
27417         this.selections= ns;
27418         this.fireEvent("selectionchange", this, this.selections);
27419     },
27420
27421     /**
27422      * Gets a template node.
27423      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27424      * @return {HTMLElement} The node or null if it wasn't found
27425      */
27426     getNode : function(nodeInfo){
27427         if(typeof nodeInfo == "string"){
27428             return document.getElementById(nodeInfo);
27429         }else if(typeof nodeInfo == "number"){
27430             return this.nodes[nodeInfo];
27431         }
27432         return nodeInfo;
27433     },
27434
27435     /**
27436      * Gets a range template nodes.
27437      * @param {Number} startIndex
27438      * @param {Number} endIndex
27439      * @return {Array} An array of nodes
27440      */
27441     getNodes : function(start, end){
27442         var ns = this.nodes;
27443         start = start || 0;
27444         end = typeof end == "undefined" ? ns.length - 1 : end;
27445         var nodes = [];
27446         if(start <= end){
27447             for(var i = start; i <= end; i++){
27448                 nodes.push(ns[i]);
27449             }
27450         } else{
27451             for(var i = start; i >= end; i--){
27452                 nodes.push(ns[i]);
27453             }
27454         }
27455         return nodes;
27456     },
27457
27458     /**
27459      * Finds the index of the passed node
27460      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27461      * @return {Number} The index of the node or -1
27462      */
27463     indexOf : function(node){
27464         node = this.getNode(node);
27465         if(typeof node.nodeIndex == "number"){
27466             return node.nodeIndex;
27467         }
27468         var ns = this.nodes;
27469         for(var i = 0, len = ns.length; i < len; i++){
27470             if(ns[i] == node){
27471                 return i;
27472             }
27473         }
27474         return -1;
27475     }
27476 });
27477 /*
27478  * Based on:
27479  * Ext JS Library 1.1.1
27480  * Copyright(c) 2006-2007, Ext JS, LLC.
27481  *
27482  * Originally Released Under LGPL - original licence link has changed is not relivant.
27483  *
27484  * Fork - LGPL
27485  * <script type="text/javascript">
27486  */
27487
27488 /**
27489  * @class Roo.JsonView
27490  * @extends Roo.View
27491  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27492 <pre><code>
27493 var view = new Roo.JsonView({
27494     container: "my-element",
27495     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27496     multiSelect: true, 
27497     jsonRoot: "data" 
27498 });
27499
27500 // listen for node click?
27501 view.on("click", function(vw, index, node, e){
27502     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27503 });
27504
27505 // direct load of JSON data
27506 view.load("foobar.php");
27507
27508 // Example from my blog list
27509 var tpl = new Roo.Template(
27510     '&lt;div class="entry"&gt;' +
27511     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27512     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27513     "&lt;/div&gt;&lt;hr /&gt;"
27514 );
27515
27516 var moreView = new Roo.JsonView({
27517     container :  "entry-list", 
27518     template : tpl,
27519     jsonRoot: "posts"
27520 });
27521 moreView.on("beforerender", this.sortEntries, this);
27522 moreView.load({
27523     url: "/blog/get-posts.php",
27524     params: "allposts=true",
27525     text: "Loading Blog Entries..."
27526 });
27527 </code></pre>
27528
27529 * Note: old code is supported with arguments : (container, template, config)
27530
27531
27532  * @constructor
27533  * Create a new JsonView
27534  * 
27535  * @param {Object} config The config object
27536  * 
27537  */
27538 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27539     
27540     
27541     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27542
27543     var um = this.el.getUpdateManager();
27544     um.setRenderer(this);
27545     um.on("update", this.onLoad, this);
27546     um.on("failure", this.onLoadException, this);
27547
27548     /**
27549      * @event beforerender
27550      * Fires before rendering of the downloaded JSON data.
27551      * @param {Roo.JsonView} this
27552      * @param {Object} data The JSON data loaded
27553      */
27554     /**
27555      * @event load
27556      * Fires when data is loaded.
27557      * @param {Roo.JsonView} this
27558      * @param {Object} data The JSON data loaded
27559      * @param {Object} response The raw Connect response object
27560      */
27561     /**
27562      * @event loadexception
27563      * Fires when loading fails.
27564      * @param {Roo.JsonView} this
27565      * @param {Object} response The raw Connect response object
27566      */
27567     this.addEvents({
27568         'beforerender' : true,
27569         'load' : true,
27570         'loadexception' : true
27571     });
27572 };
27573 Roo.extend(Roo.JsonView, Roo.View, {
27574     /**
27575      * @type {String} The root property in the loaded JSON object that contains the data
27576      */
27577     jsonRoot : "",
27578
27579     /**
27580      * Refreshes the view.
27581      */
27582     refresh : function(){
27583         this.clearSelections();
27584         this.el.update("");
27585         var html = [];
27586         var o = this.jsonData;
27587         if(o && o.length > 0){
27588             for(var i = 0, len = o.length; i < len; i++){
27589                 var data = this.prepareData(o[i], i, o);
27590                 html[html.length] = this.tpl.apply(data);
27591             }
27592         }else{
27593             html.push(this.emptyText);
27594         }
27595         this.el.update(html.join(""));
27596         this.nodes = this.el.dom.childNodes;
27597         this.updateIndexes(0);
27598     },
27599
27600     /**
27601      * 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.
27602      * @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:
27603      <pre><code>
27604      view.load({
27605          url: "your-url.php",
27606          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27607          callback: yourFunction,
27608          scope: yourObject, //(optional scope)
27609          discardUrl: false,
27610          nocache: false,
27611          text: "Loading...",
27612          timeout: 30,
27613          scripts: false
27614      });
27615      </code></pre>
27616      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27617      * 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.
27618      * @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}
27619      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27620      * @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.
27621      */
27622     load : function(){
27623         var um = this.el.getUpdateManager();
27624         um.update.apply(um, arguments);
27625     },
27626
27627     // note - render is a standard framework call...
27628     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27629     render : function(el, response){
27630         
27631         this.clearSelections();
27632         this.el.update("");
27633         var o;
27634         try{
27635             if (response != '') {
27636                 o = Roo.util.JSON.decode(response.responseText);
27637                 if(this.jsonRoot){
27638                     
27639                     o = o[this.jsonRoot];
27640                 }
27641             }
27642         } catch(e){
27643         }
27644         /**
27645          * The current JSON data or null
27646          */
27647         this.jsonData = o;
27648         this.beforeRender();
27649         this.refresh();
27650     },
27651
27652 /**
27653  * Get the number of records in the current JSON dataset
27654  * @return {Number}
27655  */
27656     getCount : function(){
27657         return this.jsonData ? this.jsonData.length : 0;
27658     },
27659
27660 /**
27661  * Returns the JSON object for the specified node(s)
27662  * @param {HTMLElement/Array} node The node or an array of nodes
27663  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27664  * you get the JSON object for the node
27665  */
27666     getNodeData : function(node){
27667         if(node instanceof Array){
27668             var data = [];
27669             for(var i = 0, len = node.length; i < len; i++){
27670                 data.push(this.getNodeData(node[i]));
27671             }
27672             return data;
27673         }
27674         return this.jsonData[this.indexOf(node)] || null;
27675     },
27676
27677     beforeRender : function(){
27678         this.snapshot = this.jsonData;
27679         if(this.sortInfo){
27680             this.sort.apply(this, this.sortInfo);
27681         }
27682         this.fireEvent("beforerender", this, this.jsonData);
27683     },
27684
27685     onLoad : function(el, o){
27686         this.fireEvent("load", this, this.jsonData, o);
27687     },
27688
27689     onLoadException : function(el, o){
27690         this.fireEvent("loadexception", this, o);
27691     },
27692
27693 /**
27694  * Filter the data by a specific property.
27695  * @param {String} property A property on your JSON objects
27696  * @param {String/RegExp} value Either string that the property values
27697  * should start with, or a RegExp to test against the property
27698  */
27699     filter : function(property, value){
27700         if(this.jsonData){
27701             var data = [];
27702             var ss = this.snapshot;
27703             if(typeof value == "string"){
27704                 var vlen = value.length;
27705                 if(vlen == 0){
27706                     this.clearFilter();
27707                     return;
27708                 }
27709                 value = value.toLowerCase();
27710                 for(var i = 0, len = ss.length; i < len; i++){
27711                     var o = ss[i];
27712                     if(o[property].substr(0, vlen).toLowerCase() == value){
27713                         data.push(o);
27714                     }
27715                 }
27716             } else if(value.exec){ // regex?
27717                 for(var i = 0, len = ss.length; i < len; i++){
27718                     var o = ss[i];
27719                     if(value.test(o[property])){
27720                         data.push(o);
27721                     }
27722                 }
27723             } else{
27724                 return;
27725             }
27726             this.jsonData = data;
27727             this.refresh();
27728         }
27729     },
27730
27731 /**
27732  * Filter by a function. The passed function will be called with each
27733  * object in the current dataset. If the function returns true the value is kept,
27734  * otherwise it is filtered.
27735  * @param {Function} fn
27736  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27737  */
27738     filterBy : function(fn, scope){
27739         if(this.jsonData){
27740             var data = [];
27741             var ss = this.snapshot;
27742             for(var i = 0, len = ss.length; i < len; i++){
27743                 var o = ss[i];
27744                 if(fn.call(scope || this, o)){
27745                     data.push(o);
27746                 }
27747             }
27748             this.jsonData = data;
27749             this.refresh();
27750         }
27751     },
27752
27753 /**
27754  * Clears the current filter.
27755  */
27756     clearFilter : function(){
27757         if(this.snapshot && this.jsonData != this.snapshot){
27758             this.jsonData = this.snapshot;
27759             this.refresh();
27760         }
27761     },
27762
27763
27764 /**
27765  * Sorts the data for this view and refreshes it.
27766  * @param {String} property A property on your JSON objects to sort on
27767  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27768  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27769  */
27770     sort : function(property, dir, sortType){
27771         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27772         if(this.jsonData){
27773             var p = property;
27774             var dsc = dir && dir.toLowerCase() == "desc";
27775             var f = function(o1, o2){
27776                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27777                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27778                 ;
27779                 if(v1 < v2){
27780                     return dsc ? +1 : -1;
27781                 } else if(v1 > v2){
27782                     return dsc ? -1 : +1;
27783                 } else{
27784                     return 0;
27785                 }
27786             };
27787             this.jsonData.sort(f);
27788             this.refresh();
27789             if(this.jsonData != this.snapshot){
27790                 this.snapshot.sort(f);
27791             }
27792         }
27793     }
27794 });/*
27795  * Based on:
27796  * Ext JS Library 1.1.1
27797  * Copyright(c) 2006-2007, Ext JS, LLC.
27798  *
27799  * Originally Released Under LGPL - original licence link has changed is not relivant.
27800  *
27801  * Fork - LGPL
27802  * <script type="text/javascript">
27803  */
27804  
27805
27806 /**
27807  * @class Roo.ColorPalette
27808  * @extends Roo.Component
27809  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27810  * Here's an example of typical usage:
27811  * <pre><code>
27812 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27813 cp.render('my-div');
27814
27815 cp.on('select', function(palette, selColor){
27816     // do something with selColor
27817 });
27818 </code></pre>
27819  * @constructor
27820  * Create a new ColorPalette
27821  * @param {Object} config The config object
27822  */
27823 Roo.ColorPalette = function(config){
27824     Roo.ColorPalette.superclass.constructor.call(this, config);
27825     this.addEvents({
27826         /**
27827              * @event select
27828              * Fires when a color is selected
27829              * @param {ColorPalette} this
27830              * @param {String} color The 6-digit color hex code (without the # symbol)
27831              */
27832         select: true
27833     });
27834
27835     if(this.handler){
27836         this.on("select", this.handler, this.scope, true);
27837     }
27838 };
27839 Roo.extend(Roo.ColorPalette, Roo.Component, {
27840     /**
27841      * @cfg {String} itemCls
27842      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27843      */
27844     itemCls : "x-color-palette",
27845     /**
27846      * @cfg {String} value
27847      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27848      * the hex codes are case-sensitive.
27849      */
27850     value : null,
27851     clickEvent:'click',
27852     // private
27853     ctype: "Roo.ColorPalette",
27854
27855     /**
27856      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27857      */
27858     allowReselect : false,
27859
27860     /**
27861      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27862      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27863      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27864      * of colors with the width setting until the box is symmetrical.</p>
27865      * <p>You can override individual colors if needed:</p>
27866      * <pre><code>
27867 var cp = new Roo.ColorPalette();
27868 cp.colors[0] = "FF0000";  // change the first box to red
27869 </code></pre>
27870
27871 Or you can provide a custom array of your own for complete control:
27872 <pre><code>
27873 var cp = new Roo.ColorPalette();
27874 cp.colors = ["000000", "993300", "333300"];
27875 </code></pre>
27876      * @type Array
27877      */
27878     colors : [
27879         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27880         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27881         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27882         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27883         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27884     ],
27885
27886     // private
27887     onRender : function(container, position){
27888         var t = new Roo.MasterTemplate(
27889             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27890         );
27891         var c = this.colors;
27892         for(var i = 0, len = c.length; i < len; i++){
27893             t.add([c[i]]);
27894         }
27895         var el = document.createElement("div");
27896         el.className = this.itemCls;
27897         t.overwrite(el);
27898         container.dom.insertBefore(el, position);
27899         this.el = Roo.get(el);
27900         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27901         if(this.clickEvent != 'click'){
27902             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27903         }
27904     },
27905
27906     // private
27907     afterRender : function(){
27908         Roo.ColorPalette.superclass.afterRender.call(this);
27909         if(this.value){
27910             var s = this.value;
27911             this.value = null;
27912             this.select(s);
27913         }
27914     },
27915
27916     // private
27917     handleClick : function(e, t){
27918         e.preventDefault();
27919         if(!this.disabled){
27920             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27921             this.select(c.toUpperCase());
27922         }
27923     },
27924
27925     /**
27926      * Selects the specified color in the palette (fires the select event)
27927      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27928      */
27929     select : function(color){
27930         color = color.replace("#", "");
27931         if(color != this.value || this.allowReselect){
27932             var el = this.el;
27933             if(this.value){
27934                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27935             }
27936             el.child("a.color-"+color).addClass("x-color-palette-sel");
27937             this.value = color;
27938             this.fireEvent("select", this, color);
27939         }
27940     }
27941 });/*
27942  * Based on:
27943  * Ext JS Library 1.1.1
27944  * Copyright(c) 2006-2007, Ext JS, LLC.
27945  *
27946  * Originally Released Under LGPL - original licence link has changed is not relivant.
27947  *
27948  * Fork - LGPL
27949  * <script type="text/javascript">
27950  */
27951  
27952 /**
27953  * @class Roo.DatePicker
27954  * @extends Roo.Component
27955  * Simple date picker class.
27956  * @constructor
27957  * Create a new DatePicker
27958  * @param {Object} config The config object
27959  */
27960 Roo.DatePicker = function(config){
27961     Roo.DatePicker.superclass.constructor.call(this, config);
27962
27963     this.value = config && config.value ?
27964                  config.value.clearTime() : new Date().clearTime();
27965
27966     this.addEvents({
27967         /**
27968              * @event select
27969              * Fires when a date is selected
27970              * @param {DatePicker} this
27971              * @param {Date} date The selected date
27972              */
27973         'select': true,
27974         /**
27975              * @event monthchange
27976              * Fires when the displayed month changes 
27977              * @param {DatePicker} this
27978              * @param {Date} date The selected month
27979              */
27980         'monthchange': true
27981     });
27982
27983     if(this.handler){
27984         this.on("select", this.handler,  this.scope || this);
27985     }
27986     // build the disabledDatesRE
27987     if(!this.disabledDatesRE && this.disabledDates){
27988         var dd = this.disabledDates;
27989         var re = "(?:";
27990         for(var i = 0; i < dd.length; i++){
27991             re += dd[i];
27992             if(i != dd.length-1) {
27993                 re += "|";
27994             }
27995         }
27996         this.disabledDatesRE = new RegExp(re + ")");
27997     }
27998 };
27999
28000 Roo.extend(Roo.DatePicker, Roo.Component, {
28001     /**
28002      * @cfg {String} todayText
28003      * The text to display on the button that selects the current date (defaults to "Today")
28004      */
28005     todayText : "Today",
28006     /**
28007      * @cfg {String} okText
28008      * The text to display on the ok button
28009      */
28010     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28011     /**
28012      * @cfg {String} cancelText
28013      * The text to display on the cancel button
28014      */
28015     cancelText : "Cancel",
28016     /**
28017      * @cfg {String} todayTip
28018      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28019      */
28020     todayTip : "{0} (Spacebar)",
28021     /**
28022      * @cfg {Date} minDate
28023      * Minimum allowable date (JavaScript date object, defaults to null)
28024      */
28025     minDate : null,
28026     /**
28027      * @cfg {Date} maxDate
28028      * Maximum allowable date (JavaScript date object, defaults to null)
28029      */
28030     maxDate : null,
28031     /**
28032      * @cfg {String} minText
28033      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28034      */
28035     minText : "This date is before the minimum date",
28036     /**
28037      * @cfg {String} maxText
28038      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28039      */
28040     maxText : "This date is after the maximum date",
28041     /**
28042      * @cfg {String} format
28043      * The default date format string which can be overriden for localization support.  The format must be
28044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28045      */
28046     format : "m/d/y",
28047     /**
28048      * @cfg {Array} disabledDays
28049      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28050      */
28051     disabledDays : null,
28052     /**
28053      * @cfg {String} disabledDaysText
28054      * The tooltip to display when the date falls on a disabled day (defaults to "")
28055      */
28056     disabledDaysText : "",
28057     /**
28058      * @cfg {RegExp} disabledDatesRE
28059      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28060      */
28061     disabledDatesRE : null,
28062     /**
28063      * @cfg {String} disabledDatesText
28064      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28065      */
28066     disabledDatesText : "",
28067     /**
28068      * @cfg {Boolean} constrainToViewport
28069      * True to constrain the date picker to the viewport (defaults to true)
28070      */
28071     constrainToViewport : true,
28072     /**
28073      * @cfg {Array} monthNames
28074      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28075      */
28076     monthNames : Date.monthNames,
28077     /**
28078      * @cfg {Array} dayNames
28079      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28080      */
28081     dayNames : Date.dayNames,
28082     /**
28083      * @cfg {String} nextText
28084      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28085      */
28086     nextText: 'Next Month (Control+Right)',
28087     /**
28088      * @cfg {String} prevText
28089      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28090      */
28091     prevText: 'Previous Month (Control+Left)',
28092     /**
28093      * @cfg {String} monthYearText
28094      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28095      */
28096     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28097     /**
28098      * @cfg {Number} startDay
28099      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28100      */
28101     startDay : 0,
28102     /**
28103      * @cfg {Bool} showClear
28104      * Show a clear button (usefull for date form elements that can be blank.)
28105      */
28106     
28107     showClear: false,
28108     
28109     /**
28110      * Sets the value of the date field
28111      * @param {Date} value The date to set
28112      */
28113     setValue : function(value){
28114         var old = this.value;
28115         
28116         if (typeof(value) == 'string') {
28117          
28118             value = Date.parseDate(value, this.format);
28119         }
28120         if (!value) {
28121             value = new Date();
28122         }
28123         
28124         this.value = value.clearTime(true);
28125         if(this.el){
28126             this.update(this.value);
28127         }
28128     },
28129
28130     /**
28131      * Gets the current selected value of the date field
28132      * @return {Date} The selected date
28133      */
28134     getValue : function(){
28135         return this.value;
28136     },
28137
28138     // private
28139     focus : function(){
28140         if(this.el){
28141             this.update(this.activeDate);
28142         }
28143     },
28144
28145     // privateval
28146     onRender : function(container, position){
28147         
28148         var m = [
28149              '<table cellspacing="0">',
28150                 '<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>',
28151                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28152         var dn = this.dayNames;
28153         for(var i = 0; i < 7; i++){
28154             var d = this.startDay+i;
28155             if(d > 6){
28156                 d = d-7;
28157             }
28158             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28159         }
28160         m[m.length] = "</tr></thead><tbody><tr>";
28161         for(var i = 0; i < 42; i++) {
28162             if(i % 7 == 0 && i != 0){
28163                 m[m.length] = "</tr><tr>";
28164             }
28165             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28166         }
28167         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28168             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28169
28170         var el = document.createElement("div");
28171         el.className = "x-date-picker";
28172         el.innerHTML = m.join("");
28173
28174         container.dom.insertBefore(el, position);
28175
28176         this.el = Roo.get(el);
28177         this.eventEl = Roo.get(el.firstChild);
28178
28179         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28180             handler: this.showPrevMonth,
28181             scope: this,
28182             preventDefault:true,
28183             stopDefault:true
28184         });
28185
28186         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28187             handler: this.showNextMonth,
28188             scope: this,
28189             preventDefault:true,
28190             stopDefault:true
28191         });
28192
28193         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28194
28195         this.monthPicker = this.el.down('div.x-date-mp');
28196         this.monthPicker.enableDisplayMode('block');
28197         
28198         var kn = new Roo.KeyNav(this.eventEl, {
28199             "left" : function(e){
28200                 e.ctrlKey ?
28201                     this.showPrevMonth() :
28202                     this.update(this.activeDate.add("d", -1));
28203             },
28204
28205             "right" : function(e){
28206                 e.ctrlKey ?
28207                     this.showNextMonth() :
28208                     this.update(this.activeDate.add("d", 1));
28209             },
28210
28211             "up" : function(e){
28212                 e.ctrlKey ?
28213                     this.showNextYear() :
28214                     this.update(this.activeDate.add("d", -7));
28215             },
28216
28217             "down" : function(e){
28218                 e.ctrlKey ?
28219                     this.showPrevYear() :
28220                     this.update(this.activeDate.add("d", 7));
28221             },
28222
28223             "pageUp" : function(e){
28224                 this.showNextMonth();
28225             },
28226
28227             "pageDown" : function(e){
28228                 this.showPrevMonth();
28229             },
28230
28231             "enter" : function(e){
28232                 e.stopPropagation();
28233                 return true;
28234             },
28235
28236             scope : this
28237         });
28238
28239         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28240
28241         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28242
28243         this.el.unselectable();
28244         
28245         this.cells = this.el.select("table.x-date-inner tbody td");
28246         this.textNodes = this.el.query("table.x-date-inner tbody span");
28247
28248         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28249             text: "&#160;",
28250             tooltip: this.monthYearText
28251         });
28252
28253         this.mbtn.on('click', this.showMonthPicker, this);
28254         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28255
28256
28257         var today = (new Date()).dateFormat(this.format);
28258         
28259         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28260         if (this.showClear) {
28261             baseTb.add( new Roo.Toolbar.Fill());
28262         }
28263         baseTb.add({
28264             text: String.format(this.todayText, today),
28265             tooltip: String.format(this.todayTip, today),
28266             handler: this.selectToday,
28267             scope: this
28268         });
28269         
28270         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28271             
28272         //});
28273         if (this.showClear) {
28274             
28275             baseTb.add( new Roo.Toolbar.Fill());
28276             baseTb.add({
28277                 text: '&#160;',
28278                 cls: 'x-btn-icon x-btn-clear',
28279                 handler: function() {
28280                     //this.value = '';
28281                     this.fireEvent("select", this, '');
28282                 },
28283                 scope: this
28284             });
28285         }
28286         
28287         
28288         if(Roo.isIE){
28289             this.el.repaint();
28290         }
28291         this.update(this.value);
28292     },
28293
28294     createMonthPicker : function(){
28295         if(!this.monthPicker.dom.firstChild){
28296             var buf = ['<table border="0" cellspacing="0">'];
28297             for(var i = 0; i < 6; i++){
28298                 buf.push(
28299                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28300                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28301                     i == 0 ?
28302                     '<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>' :
28303                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28304                 );
28305             }
28306             buf.push(
28307                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28308                     this.okText,
28309                     '</button><button type="button" class="x-date-mp-cancel">',
28310                     this.cancelText,
28311                     '</button></td></tr>',
28312                 '</table>'
28313             );
28314             this.monthPicker.update(buf.join(''));
28315             this.monthPicker.on('click', this.onMonthClick, this);
28316             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28317
28318             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28319             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28320
28321             this.mpMonths.each(function(m, a, i){
28322                 i += 1;
28323                 if((i%2) == 0){
28324                     m.dom.xmonth = 5 + Math.round(i * .5);
28325                 }else{
28326                     m.dom.xmonth = Math.round((i-1) * .5);
28327                 }
28328             });
28329         }
28330     },
28331
28332     showMonthPicker : function(){
28333         this.createMonthPicker();
28334         var size = this.el.getSize();
28335         this.monthPicker.setSize(size);
28336         this.monthPicker.child('table').setSize(size);
28337
28338         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28339         this.updateMPMonth(this.mpSelMonth);
28340         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28341         this.updateMPYear(this.mpSelYear);
28342
28343         this.monthPicker.slideIn('t', {duration:.2});
28344     },
28345
28346     updateMPYear : function(y){
28347         this.mpyear = y;
28348         var ys = this.mpYears.elements;
28349         for(var i = 1; i <= 10; i++){
28350             var td = ys[i-1], y2;
28351             if((i%2) == 0){
28352                 y2 = y + Math.round(i * .5);
28353                 td.firstChild.innerHTML = y2;
28354                 td.xyear = y2;
28355             }else{
28356                 y2 = y - (5-Math.round(i * .5));
28357                 td.firstChild.innerHTML = y2;
28358                 td.xyear = y2;
28359             }
28360             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28361         }
28362     },
28363
28364     updateMPMonth : function(sm){
28365         this.mpMonths.each(function(m, a, i){
28366             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28367         });
28368     },
28369
28370     selectMPMonth: function(m){
28371         
28372     },
28373
28374     onMonthClick : function(e, t){
28375         e.stopEvent();
28376         var el = new Roo.Element(t), pn;
28377         if(el.is('button.x-date-mp-cancel')){
28378             this.hideMonthPicker();
28379         }
28380         else if(el.is('button.x-date-mp-ok')){
28381             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28382             this.hideMonthPicker();
28383         }
28384         else if(pn = el.up('td.x-date-mp-month', 2)){
28385             this.mpMonths.removeClass('x-date-mp-sel');
28386             pn.addClass('x-date-mp-sel');
28387             this.mpSelMonth = pn.dom.xmonth;
28388         }
28389         else if(pn = el.up('td.x-date-mp-year', 2)){
28390             this.mpYears.removeClass('x-date-mp-sel');
28391             pn.addClass('x-date-mp-sel');
28392             this.mpSelYear = pn.dom.xyear;
28393         }
28394         else if(el.is('a.x-date-mp-prev')){
28395             this.updateMPYear(this.mpyear-10);
28396         }
28397         else if(el.is('a.x-date-mp-next')){
28398             this.updateMPYear(this.mpyear+10);
28399         }
28400     },
28401
28402     onMonthDblClick : function(e, t){
28403         e.stopEvent();
28404         var el = new Roo.Element(t), pn;
28405         if(pn = el.up('td.x-date-mp-month', 2)){
28406             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28407             this.hideMonthPicker();
28408         }
28409         else if(pn = el.up('td.x-date-mp-year', 2)){
28410             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28411             this.hideMonthPicker();
28412         }
28413     },
28414
28415     hideMonthPicker : function(disableAnim){
28416         if(this.monthPicker){
28417             if(disableAnim === true){
28418                 this.monthPicker.hide();
28419             }else{
28420                 this.monthPicker.slideOut('t', {duration:.2});
28421             }
28422         }
28423     },
28424
28425     // private
28426     showPrevMonth : function(e){
28427         this.update(this.activeDate.add("mo", -1));
28428     },
28429
28430     // private
28431     showNextMonth : function(e){
28432         this.update(this.activeDate.add("mo", 1));
28433     },
28434
28435     // private
28436     showPrevYear : function(){
28437         this.update(this.activeDate.add("y", -1));
28438     },
28439
28440     // private
28441     showNextYear : function(){
28442         this.update(this.activeDate.add("y", 1));
28443     },
28444
28445     // private
28446     handleMouseWheel : function(e){
28447         var delta = e.getWheelDelta();
28448         if(delta > 0){
28449             this.showPrevMonth();
28450             e.stopEvent();
28451         } else if(delta < 0){
28452             this.showNextMonth();
28453             e.stopEvent();
28454         }
28455     },
28456
28457     // private
28458     handleDateClick : function(e, t){
28459         e.stopEvent();
28460         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28461             this.setValue(new Date(t.dateValue));
28462             this.fireEvent("select", this, this.value);
28463         }
28464     },
28465
28466     // private
28467     selectToday : function(){
28468         this.setValue(new Date().clearTime());
28469         this.fireEvent("select", this, this.value);
28470     },
28471
28472     // private
28473     update : function(date)
28474     {
28475         var vd = this.activeDate;
28476         this.activeDate = date;
28477         if(vd && this.el){
28478             var t = date.getTime();
28479             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28480                 this.cells.removeClass("x-date-selected");
28481                 this.cells.each(function(c){
28482                    if(c.dom.firstChild.dateValue == t){
28483                        c.addClass("x-date-selected");
28484                        setTimeout(function(){
28485                             try{c.dom.firstChild.focus();}catch(e){}
28486                        }, 50);
28487                        return false;
28488                    }
28489                 });
28490                 return;
28491             }
28492         }
28493         
28494         var days = date.getDaysInMonth();
28495         var firstOfMonth = date.getFirstDateOfMonth();
28496         var startingPos = firstOfMonth.getDay()-this.startDay;
28497
28498         if(startingPos <= this.startDay){
28499             startingPos += 7;
28500         }
28501
28502         var pm = date.add("mo", -1);
28503         var prevStart = pm.getDaysInMonth()-startingPos;
28504
28505         var cells = this.cells.elements;
28506         var textEls = this.textNodes;
28507         days += startingPos;
28508
28509         // convert everything to numbers so it's fast
28510         var day = 86400000;
28511         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28512         var today = new Date().clearTime().getTime();
28513         var sel = date.clearTime().getTime();
28514         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28515         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28516         var ddMatch = this.disabledDatesRE;
28517         var ddText = this.disabledDatesText;
28518         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28519         var ddaysText = this.disabledDaysText;
28520         var format = this.format;
28521
28522         var setCellClass = function(cal, cell){
28523             cell.title = "";
28524             var t = d.getTime();
28525             cell.firstChild.dateValue = t;
28526             if(t == today){
28527                 cell.className += " x-date-today";
28528                 cell.title = cal.todayText;
28529             }
28530             if(t == sel){
28531                 cell.className += " x-date-selected";
28532                 setTimeout(function(){
28533                     try{cell.firstChild.focus();}catch(e){}
28534                 }, 50);
28535             }
28536             // disabling
28537             if(t < min) {
28538                 cell.className = " x-date-disabled";
28539                 cell.title = cal.minText;
28540                 return;
28541             }
28542             if(t > max) {
28543                 cell.className = " x-date-disabled";
28544                 cell.title = cal.maxText;
28545                 return;
28546             }
28547             if(ddays){
28548                 if(ddays.indexOf(d.getDay()) != -1){
28549                     cell.title = ddaysText;
28550                     cell.className = " x-date-disabled";
28551                 }
28552             }
28553             if(ddMatch && format){
28554                 var fvalue = d.dateFormat(format);
28555                 if(ddMatch.test(fvalue)){
28556                     cell.title = ddText.replace("%0", fvalue);
28557                     cell.className = " x-date-disabled";
28558                 }
28559             }
28560         };
28561
28562         var i = 0;
28563         for(; i < startingPos; i++) {
28564             textEls[i].innerHTML = (++prevStart);
28565             d.setDate(d.getDate()+1);
28566             cells[i].className = "x-date-prevday";
28567             setCellClass(this, cells[i]);
28568         }
28569         for(; i < days; i++){
28570             intDay = i - startingPos + 1;
28571             textEls[i].innerHTML = (intDay);
28572             d.setDate(d.getDate()+1);
28573             cells[i].className = "x-date-active";
28574             setCellClass(this, cells[i]);
28575         }
28576         var extraDays = 0;
28577         for(; i < 42; i++) {
28578              textEls[i].innerHTML = (++extraDays);
28579              d.setDate(d.getDate()+1);
28580              cells[i].className = "x-date-nextday";
28581              setCellClass(this, cells[i]);
28582         }
28583
28584         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28585         this.fireEvent('monthchange', this, date);
28586         
28587         if(!this.internalRender){
28588             var main = this.el.dom.firstChild;
28589             var w = main.offsetWidth;
28590             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28591             Roo.fly(main).setWidth(w);
28592             this.internalRender = true;
28593             // opera does not respect the auto grow header center column
28594             // then, after it gets a width opera refuses to recalculate
28595             // without a second pass
28596             if(Roo.isOpera && !this.secondPass){
28597                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28598                 this.secondPass = true;
28599                 this.update.defer(10, this, [date]);
28600             }
28601         }
28602         
28603         
28604     }
28605 });        /*
28606  * Based on:
28607  * Ext JS Library 1.1.1
28608  * Copyright(c) 2006-2007, Ext JS, LLC.
28609  *
28610  * Originally Released Under LGPL - original licence link has changed is not relivant.
28611  *
28612  * Fork - LGPL
28613  * <script type="text/javascript">
28614  */
28615 /**
28616  * @class Roo.TabPanel
28617  * @extends Roo.util.Observable
28618  * A lightweight tab container.
28619  * <br><br>
28620  * Usage:
28621  * <pre><code>
28622 // basic tabs 1, built from existing content
28623 var tabs = new Roo.TabPanel("tabs1");
28624 tabs.addTab("script", "View Script");
28625 tabs.addTab("markup", "View Markup");
28626 tabs.activate("script");
28627
28628 // more advanced tabs, built from javascript
28629 var jtabs = new Roo.TabPanel("jtabs");
28630 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28631
28632 // set up the UpdateManager
28633 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28634 var updater = tab2.getUpdateManager();
28635 updater.setDefaultUrl("ajax1.htm");
28636 tab2.on('activate', updater.refresh, updater, true);
28637
28638 // Use setUrl for Ajax loading
28639 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28640 tab3.setUrl("ajax2.htm", null, true);
28641
28642 // Disabled tab
28643 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28644 tab4.disable();
28645
28646 jtabs.activate("jtabs-1");
28647  * </code></pre>
28648  * @constructor
28649  * Create a new TabPanel.
28650  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28651  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28652  */
28653 Roo.TabPanel = function(container, config){
28654     /**
28655     * The container element for this TabPanel.
28656     * @type Roo.Element
28657     */
28658     this.el = Roo.get(container, true);
28659     if(config){
28660         if(typeof config == "boolean"){
28661             this.tabPosition = config ? "bottom" : "top";
28662         }else{
28663             Roo.apply(this, config);
28664         }
28665     }
28666     if(this.tabPosition == "bottom"){
28667         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28668         this.el.addClass("x-tabs-bottom");
28669     }
28670     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28671     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28672     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28673     if(Roo.isIE){
28674         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28675     }
28676     if(this.tabPosition != "bottom"){
28677         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28678          * @type Roo.Element
28679          */
28680         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28681         this.el.addClass("x-tabs-top");
28682     }
28683     this.items = [];
28684
28685     this.bodyEl.setStyle("position", "relative");
28686
28687     this.active = null;
28688     this.activateDelegate = this.activate.createDelegate(this);
28689
28690     this.addEvents({
28691         /**
28692          * @event tabchange
28693          * Fires when the active tab changes
28694          * @param {Roo.TabPanel} this
28695          * @param {Roo.TabPanelItem} activePanel The new active tab
28696          */
28697         "tabchange": true,
28698         /**
28699          * @event beforetabchange
28700          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28701          * @param {Roo.TabPanel} this
28702          * @param {Object} e Set cancel to true on this object to cancel the tab change
28703          * @param {Roo.TabPanelItem} tab The tab being changed to
28704          */
28705         "beforetabchange" : true
28706     });
28707
28708     Roo.EventManager.onWindowResize(this.onResize, this);
28709     this.cpad = this.el.getPadding("lr");
28710     this.hiddenCount = 0;
28711
28712
28713     // toolbar on the tabbar support...
28714     if (this.toolbar) {
28715         var tcfg = this.toolbar;
28716         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28717         this.toolbar = new Roo.Toolbar(tcfg);
28718         if (Roo.isSafari) {
28719             var tbl = tcfg.container.child('table', true);
28720             tbl.setAttribute('width', '100%');
28721         }
28722         
28723     }
28724    
28725
28726
28727     Roo.TabPanel.superclass.constructor.call(this);
28728 };
28729
28730 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28731     /*
28732      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28733      */
28734     tabPosition : "top",
28735     /*
28736      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28737      */
28738     currentTabWidth : 0,
28739     /*
28740      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28741      */
28742     minTabWidth : 40,
28743     /*
28744      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28745      */
28746     maxTabWidth : 250,
28747     /*
28748      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28749      */
28750     preferredTabWidth : 175,
28751     /*
28752      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28753      */
28754     resizeTabs : false,
28755     /*
28756      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28757      */
28758     monitorResize : true,
28759     /*
28760      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28761      */
28762     toolbar : false,
28763
28764     /**
28765      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28766      * @param {String} id The id of the div to use <b>or create</b>
28767      * @param {String} text The text for the tab
28768      * @param {String} content (optional) Content to put in the TabPanelItem body
28769      * @param {Boolean} closable (optional) True to create a close icon on the tab
28770      * @return {Roo.TabPanelItem} The created TabPanelItem
28771      */
28772     addTab : function(id, text, content, closable){
28773         var item = new Roo.TabPanelItem(this, id, text, closable);
28774         this.addTabItem(item);
28775         if(content){
28776             item.setContent(content);
28777         }
28778         return item;
28779     },
28780
28781     /**
28782      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28783      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28784      * @return {Roo.TabPanelItem}
28785      */
28786     getTab : function(id){
28787         return this.items[id];
28788     },
28789
28790     /**
28791      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28792      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28793      */
28794     hideTab : function(id){
28795         var t = this.items[id];
28796         if(!t.isHidden()){
28797            t.setHidden(true);
28798            this.hiddenCount++;
28799            this.autoSizeTabs();
28800         }
28801     },
28802
28803     /**
28804      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28805      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28806      */
28807     unhideTab : function(id){
28808         var t = this.items[id];
28809         if(t.isHidden()){
28810            t.setHidden(false);
28811            this.hiddenCount--;
28812            this.autoSizeTabs();
28813         }
28814     },
28815
28816     /**
28817      * Adds an existing {@link Roo.TabPanelItem}.
28818      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28819      */
28820     addTabItem : function(item){
28821         this.items[item.id] = item;
28822         this.items.push(item);
28823         if(this.resizeTabs){
28824            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28825            this.autoSizeTabs();
28826         }else{
28827             item.autoSize();
28828         }
28829     },
28830
28831     /**
28832      * Removes a {@link Roo.TabPanelItem}.
28833      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28834      */
28835     removeTab : function(id){
28836         var items = this.items;
28837         var tab = items[id];
28838         if(!tab) { return; }
28839         var index = items.indexOf(tab);
28840         if(this.active == tab && items.length > 1){
28841             var newTab = this.getNextAvailable(index);
28842             if(newTab) {
28843                 newTab.activate();
28844             }
28845         }
28846         this.stripEl.dom.removeChild(tab.pnode.dom);
28847         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28848             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28849         }
28850         items.splice(index, 1);
28851         delete this.items[tab.id];
28852         tab.fireEvent("close", tab);
28853         tab.purgeListeners();
28854         this.autoSizeTabs();
28855     },
28856
28857     getNextAvailable : function(start){
28858         var items = this.items;
28859         var index = start;
28860         // look for a next tab that will slide over to
28861         // replace the one being removed
28862         while(index < items.length){
28863             var item = items[++index];
28864             if(item && !item.isHidden()){
28865                 return item;
28866             }
28867         }
28868         // if one isn't found select the previous tab (on the left)
28869         index = start;
28870         while(index >= 0){
28871             var item = items[--index];
28872             if(item && !item.isHidden()){
28873                 return item;
28874             }
28875         }
28876         return null;
28877     },
28878
28879     /**
28880      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28881      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28882      */
28883     disableTab : function(id){
28884         var tab = this.items[id];
28885         if(tab && this.active != tab){
28886             tab.disable();
28887         }
28888     },
28889
28890     /**
28891      * Enables a {@link Roo.TabPanelItem} that is disabled.
28892      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28893      */
28894     enableTab : function(id){
28895         var tab = this.items[id];
28896         tab.enable();
28897     },
28898
28899     /**
28900      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28901      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28902      * @return {Roo.TabPanelItem} The TabPanelItem.
28903      */
28904     activate : function(id){
28905         var tab = this.items[id];
28906         if(!tab){
28907             return null;
28908         }
28909         if(tab == this.active || tab.disabled){
28910             return tab;
28911         }
28912         var e = {};
28913         this.fireEvent("beforetabchange", this, e, tab);
28914         if(e.cancel !== true && !tab.disabled){
28915             if(this.active){
28916                 this.active.hide();
28917             }
28918             this.active = this.items[id];
28919             this.active.show();
28920             this.fireEvent("tabchange", this, this.active);
28921         }
28922         return tab;
28923     },
28924
28925     /**
28926      * Gets the active {@link Roo.TabPanelItem}.
28927      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28928      */
28929     getActiveTab : function(){
28930         return this.active;
28931     },
28932
28933     /**
28934      * Updates the tab body element to fit the height of the container element
28935      * for overflow scrolling
28936      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28937      */
28938     syncHeight : function(targetHeight){
28939         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28940         var bm = this.bodyEl.getMargins();
28941         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28942         this.bodyEl.setHeight(newHeight);
28943         return newHeight;
28944     },
28945
28946     onResize : function(){
28947         if(this.monitorResize){
28948             this.autoSizeTabs();
28949         }
28950     },
28951
28952     /**
28953      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28954      */
28955     beginUpdate : function(){
28956         this.updating = true;
28957     },
28958
28959     /**
28960      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28961      */
28962     endUpdate : function(){
28963         this.updating = false;
28964         this.autoSizeTabs();
28965     },
28966
28967     /**
28968      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28969      */
28970     autoSizeTabs : function(){
28971         var count = this.items.length;
28972         var vcount = count - this.hiddenCount;
28973         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28974             return;
28975         }
28976         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28977         var availWidth = Math.floor(w / vcount);
28978         var b = this.stripBody;
28979         if(b.getWidth() > w){
28980             var tabs = this.items;
28981             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28982             if(availWidth < this.minTabWidth){
28983                 /*if(!this.sleft){    // incomplete scrolling code
28984                     this.createScrollButtons();
28985                 }
28986                 this.showScroll();
28987                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28988             }
28989         }else{
28990             if(this.currentTabWidth < this.preferredTabWidth){
28991                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28992             }
28993         }
28994     },
28995
28996     /**
28997      * Returns the number of tabs in this TabPanel.
28998      * @return {Number}
28999      */
29000      getCount : function(){
29001          return this.items.length;
29002      },
29003
29004     /**
29005      * Resizes all the tabs to the passed width
29006      * @param {Number} The new width
29007      */
29008     setTabWidth : function(width){
29009         this.currentTabWidth = width;
29010         for(var i = 0, len = this.items.length; i < len; i++) {
29011                 if(!this.items[i].isHidden()) {
29012                 this.items[i].setWidth(width);
29013             }
29014         }
29015     },
29016
29017     /**
29018      * Destroys this TabPanel
29019      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29020      */
29021     destroy : function(removeEl){
29022         Roo.EventManager.removeResizeListener(this.onResize, this);
29023         for(var i = 0, len = this.items.length; i < len; i++){
29024             this.items[i].purgeListeners();
29025         }
29026         if(removeEl === true){
29027             this.el.update("");
29028             this.el.remove();
29029         }
29030     }
29031 });
29032
29033 /**
29034  * @class Roo.TabPanelItem
29035  * @extends Roo.util.Observable
29036  * Represents an individual item (tab plus body) in a TabPanel.
29037  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29038  * @param {String} id The id of this TabPanelItem
29039  * @param {String} text The text for the tab of this TabPanelItem
29040  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29041  */
29042 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29043     /**
29044      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29045      * @type Roo.TabPanel
29046      */
29047     this.tabPanel = tabPanel;
29048     /**
29049      * The id for this TabPanelItem
29050      * @type String
29051      */
29052     this.id = id;
29053     /** @private */
29054     this.disabled = false;
29055     /** @private */
29056     this.text = text;
29057     /** @private */
29058     this.loaded = false;
29059     this.closable = closable;
29060
29061     /**
29062      * The body element for this TabPanelItem.
29063      * @type Roo.Element
29064      */
29065     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29066     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29067     this.bodyEl.setStyle("display", "block");
29068     this.bodyEl.setStyle("zoom", "1");
29069     this.hideAction();
29070
29071     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29072     /** @private */
29073     this.el = Roo.get(els.el, true);
29074     this.inner = Roo.get(els.inner, true);
29075     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29076     this.pnode = Roo.get(els.el.parentNode, true);
29077     this.el.on("mousedown", this.onTabMouseDown, this);
29078     this.el.on("click", this.onTabClick, this);
29079     /** @private */
29080     if(closable){
29081         var c = Roo.get(els.close, true);
29082         c.dom.title = this.closeText;
29083         c.addClassOnOver("close-over");
29084         c.on("click", this.closeClick, this);
29085      }
29086
29087     this.addEvents({
29088          /**
29089          * @event activate
29090          * Fires when this tab becomes the active tab.
29091          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29092          * @param {Roo.TabPanelItem} this
29093          */
29094         "activate": true,
29095         /**
29096          * @event beforeclose
29097          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29098          * @param {Roo.TabPanelItem} this
29099          * @param {Object} e Set cancel to true on this object to cancel the close.
29100          */
29101         "beforeclose": true,
29102         /**
29103          * @event close
29104          * Fires when this tab is closed.
29105          * @param {Roo.TabPanelItem} this
29106          */
29107          "close": true,
29108         /**
29109          * @event deactivate
29110          * Fires when this tab is no longer the active tab.
29111          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29112          * @param {Roo.TabPanelItem} this
29113          */
29114          "deactivate" : true
29115     });
29116     this.hidden = false;
29117
29118     Roo.TabPanelItem.superclass.constructor.call(this);
29119 };
29120
29121 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29122     purgeListeners : function(){
29123        Roo.util.Observable.prototype.purgeListeners.call(this);
29124        this.el.removeAllListeners();
29125     },
29126     /**
29127      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29128      */
29129     show : function(){
29130         this.pnode.addClass("on");
29131         this.showAction();
29132         if(Roo.isOpera){
29133             this.tabPanel.stripWrap.repaint();
29134         }
29135         this.fireEvent("activate", this.tabPanel, this);
29136     },
29137
29138     /**
29139      * Returns true if this tab is the active tab.
29140      * @return {Boolean}
29141      */
29142     isActive : function(){
29143         return this.tabPanel.getActiveTab() == this;
29144     },
29145
29146     /**
29147      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29148      */
29149     hide : function(){
29150         this.pnode.removeClass("on");
29151         this.hideAction();
29152         this.fireEvent("deactivate", this.tabPanel, this);
29153     },
29154
29155     hideAction : function(){
29156         this.bodyEl.hide();
29157         this.bodyEl.setStyle("position", "absolute");
29158         this.bodyEl.setLeft("-20000px");
29159         this.bodyEl.setTop("-20000px");
29160     },
29161
29162     showAction : function(){
29163         this.bodyEl.setStyle("position", "relative");
29164         this.bodyEl.setTop("");
29165         this.bodyEl.setLeft("");
29166         this.bodyEl.show();
29167     },
29168
29169     /**
29170      * Set the tooltip for the tab.
29171      * @param {String} tooltip The tab's tooltip
29172      */
29173     setTooltip : function(text){
29174         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29175             this.textEl.dom.qtip = text;
29176             this.textEl.dom.removeAttribute('title');
29177         }else{
29178             this.textEl.dom.title = text;
29179         }
29180     },
29181
29182     onTabClick : function(e){
29183         e.preventDefault();
29184         this.tabPanel.activate(this.id);
29185     },
29186
29187     onTabMouseDown : function(e){
29188         e.preventDefault();
29189         this.tabPanel.activate(this.id);
29190     },
29191
29192     getWidth : function(){
29193         return this.inner.getWidth();
29194     },
29195
29196     setWidth : function(width){
29197         var iwidth = width - this.pnode.getPadding("lr");
29198         this.inner.setWidth(iwidth);
29199         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29200         this.pnode.setWidth(width);
29201     },
29202
29203     /**
29204      * Show or hide the tab
29205      * @param {Boolean} hidden True to hide or false to show.
29206      */
29207     setHidden : function(hidden){
29208         this.hidden = hidden;
29209         this.pnode.setStyle("display", hidden ? "none" : "");
29210     },
29211
29212     /**
29213      * Returns true if this tab is "hidden"
29214      * @return {Boolean}
29215      */
29216     isHidden : function(){
29217         return this.hidden;
29218     },
29219
29220     /**
29221      * Returns the text for this tab
29222      * @return {String}
29223      */
29224     getText : function(){
29225         return this.text;
29226     },
29227
29228     autoSize : function(){
29229         //this.el.beginMeasure();
29230         this.textEl.setWidth(1);
29231         /*
29232          *  #2804 [new] Tabs in Roojs
29233          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29234          */
29235         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29236         //this.el.endMeasure();
29237     },
29238
29239     /**
29240      * Sets the text for the tab (Note: this also sets the tooltip text)
29241      * @param {String} text The tab's text and tooltip
29242      */
29243     setText : function(text){
29244         this.text = text;
29245         this.textEl.update(text);
29246         this.setTooltip(text);
29247         if(!this.tabPanel.resizeTabs){
29248             this.autoSize();
29249         }
29250     },
29251     /**
29252      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29253      */
29254     activate : function(){
29255         this.tabPanel.activate(this.id);
29256     },
29257
29258     /**
29259      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29260      */
29261     disable : function(){
29262         if(this.tabPanel.active != this){
29263             this.disabled = true;
29264             this.pnode.addClass("disabled");
29265         }
29266     },
29267
29268     /**
29269      * Enables this TabPanelItem if it was previously disabled.
29270      */
29271     enable : function(){
29272         this.disabled = false;
29273         this.pnode.removeClass("disabled");
29274     },
29275
29276     /**
29277      * Sets the content for this TabPanelItem.
29278      * @param {String} content The content
29279      * @param {Boolean} loadScripts true to look for and load scripts
29280      */
29281     setContent : function(content, loadScripts){
29282         this.bodyEl.update(content, loadScripts);
29283     },
29284
29285     /**
29286      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29287      * @return {Roo.UpdateManager} The UpdateManager
29288      */
29289     getUpdateManager : function(){
29290         return this.bodyEl.getUpdateManager();
29291     },
29292
29293     /**
29294      * Set a URL to be used to load the content for this TabPanelItem.
29295      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29296      * @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)
29297      * @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)
29298      * @return {Roo.UpdateManager} The UpdateManager
29299      */
29300     setUrl : function(url, params, loadOnce){
29301         if(this.refreshDelegate){
29302             this.un('activate', this.refreshDelegate);
29303         }
29304         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29305         this.on("activate", this.refreshDelegate);
29306         return this.bodyEl.getUpdateManager();
29307     },
29308
29309     /** @private */
29310     _handleRefresh : function(url, params, loadOnce){
29311         if(!loadOnce || !this.loaded){
29312             var updater = this.bodyEl.getUpdateManager();
29313             updater.update(url, params, this._setLoaded.createDelegate(this));
29314         }
29315     },
29316
29317     /**
29318      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29319      *   Will fail silently if the setUrl method has not been called.
29320      *   This does not activate the panel, just updates its content.
29321      */
29322     refresh : function(){
29323         if(this.refreshDelegate){
29324            this.loaded = false;
29325            this.refreshDelegate();
29326         }
29327     },
29328
29329     /** @private */
29330     _setLoaded : function(){
29331         this.loaded = true;
29332     },
29333
29334     /** @private */
29335     closeClick : function(e){
29336         var o = {};
29337         e.stopEvent();
29338         this.fireEvent("beforeclose", this, o);
29339         if(o.cancel !== true){
29340             this.tabPanel.removeTab(this.id);
29341         }
29342     },
29343     /**
29344      * The text displayed in the tooltip for the close icon.
29345      * @type String
29346      */
29347     closeText : "Close this tab"
29348 });
29349
29350 /** @private */
29351 Roo.TabPanel.prototype.createStrip = function(container){
29352     var strip = document.createElement("div");
29353     strip.className = "x-tabs-wrap";
29354     container.appendChild(strip);
29355     return strip;
29356 };
29357 /** @private */
29358 Roo.TabPanel.prototype.createStripList = function(strip){
29359     // div wrapper for retard IE
29360     // returns the "tr" element.
29361     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29362         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29363         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29364     return strip.firstChild.firstChild.firstChild.firstChild;
29365 };
29366 /** @private */
29367 Roo.TabPanel.prototype.createBody = function(container){
29368     var body = document.createElement("div");
29369     Roo.id(body, "tab-body");
29370     Roo.fly(body).addClass("x-tabs-body");
29371     container.appendChild(body);
29372     return body;
29373 };
29374 /** @private */
29375 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29376     var body = Roo.getDom(id);
29377     if(!body){
29378         body = document.createElement("div");
29379         body.id = id;
29380     }
29381     Roo.fly(body).addClass("x-tabs-item-body");
29382     bodyEl.insertBefore(body, bodyEl.firstChild);
29383     return body;
29384 };
29385 /** @private */
29386 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29387     var td = document.createElement("td");
29388     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29389     //stripEl.appendChild(td);
29390     if(closable){
29391         td.className = "x-tabs-closable";
29392         if(!this.closeTpl){
29393             this.closeTpl = new Roo.Template(
29394                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29395                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29396                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29397             );
29398         }
29399         var el = this.closeTpl.overwrite(td, {"text": text});
29400         var close = el.getElementsByTagName("div")[0];
29401         var inner = el.getElementsByTagName("em")[0];
29402         return {"el": el, "close": close, "inner": inner};
29403     } else {
29404         if(!this.tabTpl){
29405             this.tabTpl = new Roo.Template(
29406                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29407                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29408             );
29409         }
29410         var el = this.tabTpl.overwrite(td, {"text": text});
29411         var inner = el.getElementsByTagName("em")[0];
29412         return {"el": el, "inner": inner};
29413     }
29414 };/*
29415  * Based on:
29416  * Ext JS Library 1.1.1
29417  * Copyright(c) 2006-2007, Ext JS, LLC.
29418  *
29419  * Originally Released Under LGPL - original licence link has changed is not relivant.
29420  *
29421  * Fork - LGPL
29422  * <script type="text/javascript">
29423  */
29424
29425 /**
29426  * @class Roo.Button
29427  * @extends Roo.util.Observable
29428  * Simple Button class
29429  * @cfg {String} text The button text
29430  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29431  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29432  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29433  * @cfg {Object} scope The scope of the handler
29434  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29435  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29436  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29437  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29438  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29439  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29440    applies if enableToggle = true)
29441  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29442  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29443   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29444  * @constructor
29445  * Create a new button
29446  * @param {Object} config The config object
29447  */
29448 Roo.Button = function(renderTo, config)
29449 {
29450     if (!config) {
29451         config = renderTo;
29452         renderTo = config.renderTo || false;
29453     }
29454     
29455     Roo.apply(this, config);
29456     this.addEvents({
29457         /**
29458              * @event click
29459              * Fires when this button is clicked
29460              * @param {Button} this
29461              * @param {EventObject} e The click event
29462              */
29463             "click" : true,
29464         /**
29465              * @event toggle
29466              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29467              * @param {Button} this
29468              * @param {Boolean} pressed
29469              */
29470             "toggle" : true,
29471         /**
29472              * @event mouseover
29473              * Fires when the mouse hovers over the button
29474              * @param {Button} this
29475              * @param {Event} e The event object
29476              */
29477         'mouseover' : true,
29478         /**
29479              * @event mouseout
29480              * Fires when the mouse exits the button
29481              * @param {Button} this
29482              * @param {Event} e The event object
29483              */
29484         'mouseout': true,
29485          /**
29486              * @event render
29487              * Fires when the button is rendered
29488              * @param {Button} this
29489              */
29490         'render': true
29491     });
29492     if(this.menu){
29493         this.menu = Roo.menu.MenuMgr.get(this.menu);
29494     }
29495     // register listeners first!!  - so render can be captured..
29496     Roo.util.Observable.call(this);
29497     if(renderTo){
29498         this.render(renderTo);
29499     }
29500     
29501   
29502 };
29503
29504 Roo.extend(Roo.Button, Roo.util.Observable, {
29505     /**
29506      * 
29507      */
29508     
29509     /**
29510      * Read-only. True if this button is hidden
29511      * @type Boolean
29512      */
29513     hidden : false,
29514     /**
29515      * Read-only. True if this button is disabled
29516      * @type Boolean
29517      */
29518     disabled : false,
29519     /**
29520      * Read-only. True if this button is pressed (only if enableToggle = true)
29521      * @type Boolean
29522      */
29523     pressed : false,
29524
29525     /**
29526      * @cfg {Number} tabIndex 
29527      * The DOM tabIndex for this button (defaults to undefined)
29528      */
29529     tabIndex : undefined,
29530
29531     /**
29532      * @cfg {Boolean} enableToggle
29533      * True to enable pressed/not pressed toggling (defaults to false)
29534      */
29535     enableToggle: false,
29536     /**
29537      * @cfg {Roo.menu.Menu} menu
29538      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29539      */
29540     menu : undefined,
29541     /**
29542      * @cfg {String} menuAlign
29543      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29544      */
29545     menuAlign : "tl-bl?",
29546
29547     /**
29548      * @cfg {String} iconCls
29549      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29550      */
29551     iconCls : undefined,
29552     /**
29553      * @cfg {String} type
29554      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29555      */
29556     type : 'button',
29557
29558     // private
29559     menuClassTarget: 'tr',
29560
29561     /**
29562      * @cfg {String} clickEvent
29563      * The type of event to map to the button's event handler (defaults to 'click')
29564      */
29565     clickEvent : 'click',
29566
29567     /**
29568      * @cfg {Boolean} handleMouseEvents
29569      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29570      */
29571     handleMouseEvents : true,
29572
29573     /**
29574      * @cfg {String} tooltipType
29575      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29576      */
29577     tooltipType : 'qtip',
29578
29579     /**
29580      * @cfg {String} cls
29581      * A CSS class to apply to the button's main element.
29582      */
29583     
29584     /**
29585      * @cfg {Roo.Template} template (Optional)
29586      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29587      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29588      * require code modifications if required elements (e.g. a button) aren't present.
29589      */
29590
29591     // private
29592     render : function(renderTo){
29593         var btn;
29594         if(this.hideParent){
29595             this.parentEl = Roo.get(renderTo);
29596         }
29597         if(!this.dhconfig){
29598             if(!this.template){
29599                 if(!Roo.Button.buttonTemplate){
29600                     // hideous table template
29601                     Roo.Button.buttonTemplate = new Roo.Template(
29602                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29603                         '<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>',
29604                         "</tr></tbody></table>");
29605                 }
29606                 this.template = Roo.Button.buttonTemplate;
29607             }
29608             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29609             var btnEl = btn.child("button:first");
29610             btnEl.on('focus', this.onFocus, this);
29611             btnEl.on('blur', this.onBlur, this);
29612             if(this.cls){
29613                 btn.addClass(this.cls);
29614             }
29615             if(this.icon){
29616                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29617             }
29618             if(this.iconCls){
29619                 btnEl.addClass(this.iconCls);
29620                 if(!this.cls){
29621                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29622                 }
29623             }
29624             if(this.tabIndex !== undefined){
29625                 btnEl.dom.tabIndex = this.tabIndex;
29626             }
29627             if(this.tooltip){
29628                 if(typeof this.tooltip == 'object'){
29629                     Roo.QuickTips.tips(Roo.apply({
29630                           target: btnEl.id
29631                     }, this.tooltip));
29632                 } else {
29633                     btnEl.dom[this.tooltipType] = this.tooltip;
29634                 }
29635             }
29636         }else{
29637             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29638         }
29639         this.el = btn;
29640         if(this.id){
29641             this.el.dom.id = this.el.id = this.id;
29642         }
29643         if(this.menu){
29644             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29645             this.menu.on("show", this.onMenuShow, this);
29646             this.menu.on("hide", this.onMenuHide, this);
29647         }
29648         btn.addClass("x-btn");
29649         if(Roo.isIE && !Roo.isIE7){
29650             this.autoWidth.defer(1, this);
29651         }else{
29652             this.autoWidth();
29653         }
29654         if(this.handleMouseEvents){
29655             btn.on("mouseover", this.onMouseOver, this);
29656             btn.on("mouseout", this.onMouseOut, this);
29657             btn.on("mousedown", this.onMouseDown, this);
29658         }
29659         btn.on(this.clickEvent, this.onClick, this);
29660         //btn.on("mouseup", this.onMouseUp, this);
29661         if(this.hidden){
29662             this.hide();
29663         }
29664         if(this.disabled){
29665             this.disable();
29666         }
29667         Roo.ButtonToggleMgr.register(this);
29668         if(this.pressed){
29669             this.el.addClass("x-btn-pressed");
29670         }
29671         if(this.repeat){
29672             var repeater = new Roo.util.ClickRepeater(btn,
29673                 typeof this.repeat == "object" ? this.repeat : {}
29674             );
29675             repeater.on("click", this.onClick,  this);
29676         }
29677         
29678         this.fireEvent('render', this);
29679         
29680     },
29681     /**
29682      * Returns the button's underlying element
29683      * @return {Roo.Element} The element
29684      */
29685     getEl : function(){
29686         return this.el;  
29687     },
29688     
29689     /**
29690      * Destroys this Button and removes any listeners.
29691      */
29692     destroy : function(){
29693         Roo.ButtonToggleMgr.unregister(this);
29694         this.el.removeAllListeners();
29695         this.purgeListeners();
29696         this.el.remove();
29697     },
29698
29699     // private
29700     autoWidth : function(){
29701         if(this.el){
29702             this.el.setWidth("auto");
29703             if(Roo.isIE7 && Roo.isStrict){
29704                 var ib = this.el.child('button');
29705                 if(ib && ib.getWidth() > 20){
29706                     ib.clip();
29707                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29708                 }
29709             }
29710             if(this.minWidth){
29711                 if(this.hidden){
29712                     this.el.beginMeasure();
29713                 }
29714                 if(this.el.getWidth() < this.minWidth){
29715                     this.el.setWidth(this.minWidth);
29716                 }
29717                 if(this.hidden){
29718                     this.el.endMeasure();
29719                 }
29720             }
29721         }
29722     },
29723
29724     /**
29725      * Assigns this button's click handler
29726      * @param {Function} handler The function to call when the button is clicked
29727      * @param {Object} scope (optional) Scope for the function passed in
29728      */
29729     setHandler : function(handler, scope){
29730         this.handler = handler;
29731         this.scope = scope;  
29732     },
29733     
29734     /**
29735      * Sets this button's text
29736      * @param {String} text The button text
29737      */
29738     setText : function(text){
29739         this.text = text;
29740         if(this.el){
29741             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29742         }
29743         this.autoWidth();
29744     },
29745     
29746     /**
29747      * Gets the text for this button
29748      * @return {String} The button text
29749      */
29750     getText : function(){
29751         return this.text;  
29752     },
29753     
29754     /**
29755      * Show this button
29756      */
29757     show: function(){
29758         this.hidden = false;
29759         if(this.el){
29760             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29761         }
29762     },
29763     
29764     /**
29765      * Hide this button
29766      */
29767     hide: function(){
29768         this.hidden = true;
29769         if(this.el){
29770             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29771         }
29772     },
29773     
29774     /**
29775      * Convenience function for boolean show/hide
29776      * @param {Boolean} visible True to show, false to hide
29777      */
29778     setVisible: function(visible){
29779         if(visible) {
29780             this.show();
29781         }else{
29782             this.hide();
29783         }
29784     },
29785     
29786     /**
29787      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29788      * @param {Boolean} state (optional) Force a particular state
29789      */
29790     toggle : function(state){
29791         state = state === undefined ? !this.pressed : state;
29792         if(state != this.pressed){
29793             if(state){
29794                 this.el.addClass("x-btn-pressed");
29795                 this.pressed = true;
29796                 this.fireEvent("toggle", this, true);
29797             }else{
29798                 this.el.removeClass("x-btn-pressed");
29799                 this.pressed = false;
29800                 this.fireEvent("toggle", this, false);
29801             }
29802             if(this.toggleHandler){
29803                 this.toggleHandler.call(this.scope || this, this, state);
29804             }
29805         }
29806     },
29807     
29808     /**
29809      * Focus the button
29810      */
29811     focus : function(){
29812         this.el.child('button:first').focus();
29813     },
29814     
29815     /**
29816      * Disable this button
29817      */
29818     disable : function(){
29819         if(this.el){
29820             this.el.addClass("x-btn-disabled");
29821         }
29822         this.disabled = true;
29823     },
29824     
29825     /**
29826      * Enable this button
29827      */
29828     enable : function(){
29829         if(this.el){
29830             this.el.removeClass("x-btn-disabled");
29831         }
29832         this.disabled = false;
29833     },
29834
29835     /**
29836      * Convenience function for boolean enable/disable
29837      * @param {Boolean} enabled True to enable, false to disable
29838      */
29839     setDisabled : function(v){
29840         this[v !== true ? "enable" : "disable"]();
29841     },
29842
29843     // private
29844     onClick : function(e)
29845     {
29846         if(e){
29847             e.preventDefault();
29848         }
29849         if(e.button != 0){
29850             return;
29851         }
29852         if(!this.disabled){
29853             if(this.enableToggle){
29854                 this.toggle();
29855             }
29856             if(this.menu && !this.menu.isVisible()){
29857                 this.menu.show(this.el, this.menuAlign);
29858             }
29859             this.fireEvent("click", this, e);
29860             if(this.handler){
29861                 this.el.removeClass("x-btn-over");
29862                 this.handler.call(this.scope || this, this, e);
29863             }
29864         }
29865     },
29866     // private
29867     onMouseOver : function(e){
29868         if(!this.disabled){
29869             this.el.addClass("x-btn-over");
29870             this.fireEvent('mouseover', this, e);
29871         }
29872     },
29873     // private
29874     onMouseOut : function(e){
29875         if(!e.within(this.el,  true)){
29876             this.el.removeClass("x-btn-over");
29877             this.fireEvent('mouseout', this, e);
29878         }
29879     },
29880     // private
29881     onFocus : function(e){
29882         if(!this.disabled){
29883             this.el.addClass("x-btn-focus");
29884         }
29885     },
29886     // private
29887     onBlur : function(e){
29888         this.el.removeClass("x-btn-focus");
29889     },
29890     // private
29891     onMouseDown : function(e){
29892         if(!this.disabled && e.button == 0){
29893             this.el.addClass("x-btn-click");
29894             Roo.get(document).on('mouseup', this.onMouseUp, this);
29895         }
29896     },
29897     // private
29898     onMouseUp : function(e){
29899         if(e.button == 0){
29900             this.el.removeClass("x-btn-click");
29901             Roo.get(document).un('mouseup', this.onMouseUp, this);
29902         }
29903     },
29904     // private
29905     onMenuShow : function(e){
29906         this.el.addClass("x-btn-menu-active");
29907     },
29908     // private
29909     onMenuHide : function(e){
29910         this.el.removeClass("x-btn-menu-active");
29911     }   
29912 });
29913
29914 // Private utility class used by Button
29915 Roo.ButtonToggleMgr = function(){
29916    var groups = {};
29917    
29918    function toggleGroup(btn, state){
29919        if(state){
29920            var g = groups[btn.toggleGroup];
29921            for(var i = 0, l = g.length; i < l; i++){
29922                if(g[i] != btn){
29923                    g[i].toggle(false);
29924                }
29925            }
29926        }
29927    }
29928    
29929    return {
29930        register : function(btn){
29931            if(!btn.toggleGroup){
29932                return;
29933            }
29934            var g = groups[btn.toggleGroup];
29935            if(!g){
29936                g = groups[btn.toggleGroup] = [];
29937            }
29938            g.push(btn);
29939            btn.on("toggle", toggleGroup);
29940        },
29941        
29942        unregister : function(btn){
29943            if(!btn.toggleGroup){
29944                return;
29945            }
29946            var g = groups[btn.toggleGroup];
29947            if(g){
29948                g.remove(btn);
29949                btn.un("toggle", toggleGroup);
29950            }
29951        }
29952    };
29953 }();/*
29954  * Based on:
29955  * Ext JS Library 1.1.1
29956  * Copyright(c) 2006-2007, Ext JS, LLC.
29957  *
29958  * Originally Released Under LGPL - original licence link has changed is not relivant.
29959  *
29960  * Fork - LGPL
29961  * <script type="text/javascript">
29962  */
29963  
29964 /**
29965  * @class Roo.SplitButton
29966  * @extends Roo.Button
29967  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29968  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29969  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29970  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29971  * @cfg {String} arrowTooltip The title attribute of the arrow
29972  * @constructor
29973  * Create a new menu button
29974  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29975  * @param {Object} config The config object
29976  */
29977 Roo.SplitButton = function(renderTo, config){
29978     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29979     /**
29980      * @event arrowclick
29981      * Fires when this button's arrow is clicked
29982      * @param {SplitButton} this
29983      * @param {EventObject} e The click event
29984      */
29985     this.addEvents({"arrowclick":true});
29986 };
29987
29988 Roo.extend(Roo.SplitButton, Roo.Button, {
29989     render : function(renderTo){
29990         // this is one sweet looking template!
29991         var tpl = new Roo.Template(
29992             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29993             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29994             '<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>',
29995             "</tbody></table></td><td>",
29996             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29997             '<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>',
29998             "</tbody></table></td></tr></table>"
29999         );
30000         var btn = tpl.append(renderTo, [this.text, this.type], true);
30001         var btnEl = btn.child("button");
30002         if(this.cls){
30003             btn.addClass(this.cls);
30004         }
30005         if(this.icon){
30006             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30007         }
30008         if(this.iconCls){
30009             btnEl.addClass(this.iconCls);
30010             if(!this.cls){
30011                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30012             }
30013         }
30014         this.el = btn;
30015         if(this.handleMouseEvents){
30016             btn.on("mouseover", this.onMouseOver, this);
30017             btn.on("mouseout", this.onMouseOut, this);
30018             btn.on("mousedown", this.onMouseDown, this);
30019             btn.on("mouseup", this.onMouseUp, this);
30020         }
30021         btn.on(this.clickEvent, this.onClick, this);
30022         if(this.tooltip){
30023             if(typeof this.tooltip == 'object'){
30024                 Roo.QuickTips.tips(Roo.apply({
30025                       target: btnEl.id
30026                 }, this.tooltip));
30027             } else {
30028                 btnEl.dom[this.tooltipType] = this.tooltip;
30029             }
30030         }
30031         if(this.arrowTooltip){
30032             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30033         }
30034         if(this.hidden){
30035             this.hide();
30036         }
30037         if(this.disabled){
30038             this.disable();
30039         }
30040         if(this.pressed){
30041             this.el.addClass("x-btn-pressed");
30042         }
30043         if(Roo.isIE && !Roo.isIE7){
30044             this.autoWidth.defer(1, this);
30045         }else{
30046             this.autoWidth();
30047         }
30048         if(this.menu){
30049             this.menu.on("show", this.onMenuShow, this);
30050             this.menu.on("hide", this.onMenuHide, this);
30051         }
30052         this.fireEvent('render', this);
30053     },
30054
30055     // private
30056     autoWidth : function(){
30057         if(this.el){
30058             var tbl = this.el.child("table:first");
30059             var tbl2 = this.el.child("table:last");
30060             this.el.setWidth("auto");
30061             tbl.setWidth("auto");
30062             if(Roo.isIE7 && Roo.isStrict){
30063                 var ib = this.el.child('button:first');
30064                 if(ib && ib.getWidth() > 20){
30065                     ib.clip();
30066                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30067                 }
30068             }
30069             if(this.minWidth){
30070                 if(this.hidden){
30071                     this.el.beginMeasure();
30072                 }
30073                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30074                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30075                 }
30076                 if(this.hidden){
30077                     this.el.endMeasure();
30078                 }
30079             }
30080             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30081         } 
30082     },
30083     /**
30084      * Sets this button's click handler
30085      * @param {Function} handler The function to call when the button is clicked
30086      * @param {Object} scope (optional) Scope for the function passed above
30087      */
30088     setHandler : function(handler, scope){
30089         this.handler = handler;
30090         this.scope = scope;  
30091     },
30092     
30093     /**
30094      * Sets this button's arrow click handler
30095      * @param {Function} handler The function to call when the arrow is clicked
30096      * @param {Object} scope (optional) Scope for the function passed above
30097      */
30098     setArrowHandler : function(handler, scope){
30099         this.arrowHandler = handler;
30100         this.scope = scope;  
30101     },
30102     
30103     /**
30104      * Focus the button
30105      */
30106     focus : function(){
30107         if(this.el){
30108             this.el.child("button:first").focus();
30109         }
30110     },
30111
30112     // private
30113     onClick : function(e){
30114         e.preventDefault();
30115         if(!this.disabled){
30116             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30117                 if(this.menu && !this.menu.isVisible()){
30118                     this.menu.show(this.el, this.menuAlign);
30119                 }
30120                 this.fireEvent("arrowclick", this, e);
30121                 if(this.arrowHandler){
30122                     this.arrowHandler.call(this.scope || this, this, e);
30123                 }
30124             }else{
30125                 this.fireEvent("click", this, e);
30126                 if(this.handler){
30127                     this.handler.call(this.scope || this, this, e);
30128                 }
30129             }
30130         }
30131     },
30132     // private
30133     onMouseDown : function(e){
30134         if(!this.disabled){
30135             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30136         }
30137     },
30138     // private
30139     onMouseUp : function(e){
30140         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30141     }   
30142 });
30143
30144
30145 // backwards compat
30146 Roo.MenuButton = Roo.SplitButton;/*
30147  * Based on:
30148  * Ext JS Library 1.1.1
30149  * Copyright(c) 2006-2007, Ext JS, LLC.
30150  *
30151  * Originally Released Under LGPL - original licence link has changed is not relivant.
30152  *
30153  * Fork - LGPL
30154  * <script type="text/javascript">
30155  */
30156
30157 /**
30158  * @class Roo.Toolbar
30159  * @children   Roo.Toolbar.Item Roo.form.Field
30160  * Basic Toolbar class.
30161  * @constructor
30162  * Creates a new Toolbar
30163  * @param {Object} container The config object
30164  */ 
30165 Roo.Toolbar = function(container, buttons, config)
30166 {
30167     /// old consturctor format still supported..
30168     if(container instanceof Array){ // omit the container for later rendering
30169         buttons = container;
30170         config = buttons;
30171         container = null;
30172     }
30173     if (typeof(container) == 'object' && container.xtype) {
30174         config = container;
30175         container = config.container;
30176         buttons = config.buttons || []; // not really - use items!!
30177     }
30178     var xitems = [];
30179     if (config && config.items) {
30180         xitems = config.items;
30181         delete config.items;
30182     }
30183     Roo.apply(this, config);
30184     this.buttons = buttons;
30185     
30186     if(container){
30187         this.render(container);
30188     }
30189     this.xitems = xitems;
30190     Roo.each(xitems, function(b) {
30191         this.add(b);
30192     }, this);
30193     
30194 };
30195
30196 Roo.Toolbar.prototype = {
30197     /**
30198      * @cfg {Array} items
30199      * array of button configs or elements to add (will be converted to a MixedCollection)
30200      */
30201     items: false,
30202     /**
30203      * @cfg {String/HTMLElement/Element} container
30204      * The id or element that will contain the toolbar
30205      */
30206     // private
30207     render : function(ct){
30208         this.el = Roo.get(ct);
30209         if(this.cls){
30210             this.el.addClass(this.cls);
30211         }
30212         // using a table allows for vertical alignment
30213         // 100% width is needed by Safari...
30214         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30215         this.tr = this.el.child("tr", true);
30216         var autoId = 0;
30217         this.items = new Roo.util.MixedCollection(false, function(o){
30218             return o.id || ("item" + (++autoId));
30219         });
30220         if(this.buttons){
30221             this.add.apply(this, this.buttons);
30222             delete this.buttons;
30223         }
30224     },
30225
30226     /**
30227      * Adds element(s) to the toolbar -- this function takes a variable number of 
30228      * arguments of mixed type and adds them to the toolbar.
30229      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30230      * <ul>
30231      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30232      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30233      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30234      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30235      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30236      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30237      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30238      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30239      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30240      * </ul>
30241      * @param {Mixed} arg2
30242      * @param {Mixed} etc.
30243      */
30244     add : function(){
30245         var a = arguments, l = a.length;
30246         for(var i = 0; i < l; i++){
30247             this._add(a[i]);
30248         }
30249     },
30250     // private..
30251     _add : function(el) {
30252         
30253         if (el.xtype) {
30254             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30255         }
30256         
30257         if (el.applyTo){ // some kind of form field
30258             return this.addField(el);
30259         } 
30260         if (el.render){ // some kind of Toolbar.Item
30261             return this.addItem(el);
30262         }
30263         if (typeof el == "string"){ // string
30264             if(el == "separator" || el == "-"){
30265                 return this.addSeparator();
30266             }
30267             if (el == " "){
30268                 return this.addSpacer();
30269             }
30270             if(el == "->"){
30271                 return this.addFill();
30272             }
30273             return this.addText(el);
30274             
30275         }
30276         if(el.tagName){ // element
30277             return this.addElement(el);
30278         }
30279         if(typeof el == "object"){ // must be button config?
30280             return this.addButton(el);
30281         }
30282         // and now what?!?!
30283         return false;
30284         
30285     },
30286     
30287     /**
30288      * Add an Xtype element
30289      * @param {Object} xtype Xtype Object
30290      * @return {Object} created Object
30291      */
30292     addxtype : function(e){
30293         return this.add(e);  
30294     },
30295     
30296     /**
30297      * Returns the Element for this toolbar.
30298      * @return {Roo.Element}
30299      */
30300     getEl : function(){
30301         return this.el;  
30302     },
30303     
30304     /**
30305      * Adds a separator
30306      * @return {Roo.Toolbar.Item} The separator item
30307      */
30308     addSeparator : function(){
30309         return this.addItem(new Roo.Toolbar.Separator());
30310     },
30311
30312     /**
30313      * Adds a spacer element
30314      * @return {Roo.Toolbar.Spacer} The spacer item
30315      */
30316     addSpacer : function(){
30317         return this.addItem(new Roo.Toolbar.Spacer());
30318     },
30319
30320     /**
30321      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30322      * @return {Roo.Toolbar.Fill} The fill item
30323      */
30324     addFill : function(){
30325         return this.addItem(new Roo.Toolbar.Fill());
30326     },
30327
30328     /**
30329      * Adds any standard HTML element to the toolbar
30330      * @param {String/HTMLElement/Element} el The element or id of the element to add
30331      * @return {Roo.Toolbar.Item} The element's item
30332      */
30333     addElement : function(el){
30334         return this.addItem(new Roo.Toolbar.Item(el));
30335     },
30336     /**
30337      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30338      * @type Roo.util.MixedCollection  
30339      */
30340     items : false,
30341      
30342     /**
30343      * Adds any Toolbar.Item or subclass
30344      * @param {Roo.Toolbar.Item} item
30345      * @return {Roo.Toolbar.Item} The item
30346      */
30347     addItem : function(item){
30348         var td = this.nextBlock();
30349         item.render(td);
30350         this.items.add(item);
30351         return item;
30352     },
30353     
30354     /**
30355      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30356      * @param {Object/Array} config A button config or array of configs
30357      * @return {Roo.Toolbar.Button/Array}
30358      */
30359     addButton : function(config){
30360         if(config instanceof Array){
30361             var buttons = [];
30362             for(var i = 0, len = config.length; i < len; i++) {
30363                 buttons.push(this.addButton(config[i]));
30364             }
30365             return buttons;
30366         }
30367         var b = config;
30368         if(!(config instanceof Roo.Toolbar.Button)){
30369             b = config.split ?
30370                 new Roo.Toolbar.SplitButton(config) :
30371                 new Roo.Toolbar.Button(config);
30372         }
30373         var td = this.nextBlock();
30374         b.render(td);
30375         this.items.add(b);
30376         return b;
30377     },
30378     
30379     /**
30380      * Adds text to the toolbar
30381      * @param {String} text The text to add
30382      * @return {Roo.Toolbar.Item} The element's item
30383      */
30384     addText : function(text){
30385         return this.addItem(new Roo.Toolbar.TextItem(text));
30386     },
30387     
30388     /**
30389      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30390      * @param {Number} index The index where the item is to be inserted
30391      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30392      * @return {Roo.Toolbar.Button/Item}
30393      */
30394     insertButton : function(index, item){
30395         if(item instanceof Array){
30396             var buttons = [];
30397             for(var i = 0, len = item.length; i < len; i++) {
30398                buttons.push(this.insertButton(index + i, item[i]));
30399             }
30400             return buttons;
30401         }
30402         if (!(item instanceof Roo.Toolbar.Button)){
30403            item = new Roo.Toolbar.Button(item);
30404         }
30405         var td = document.createElement("td");
30406         this.tr.insertBefore(td, this.tr.childNodes[index]);
30407         item.render(td);
30408         this.items.insert(index, item);
30409         return item;
30410     },
30411     
30412     /**
30413      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30414      * @param {Object} config
30415      * @return {Roo.Toolbar.Item} The element's item
30416      */
30417     addDom : function(config, returnEl){
30418         var td = this.nextBlock();
30419         Roo.DomHelper.overwrite(td, config);
30420         var ti = new Roo.Toolbar.Item(td.firstChild);
30421         ti.render(td);
30422         this.items.add(ti);
30423         return ti;
30424     },
30425
30426     /**
30427      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30428      * @type Roo.util.MixedCollection  
30429      */
30430     fields : false,
30431     
30432     /**
30433      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30434      * Note: the field should not have been rendered yet. For a field that has already been
30435      * rendered, use {@link #addElement}.
30436      * @param {Roo.form.Field} field
30437      * @return {Roo.ToolbarItem}
30438      */
30439      
30440       
30441     addField : function(field) {
30442         if (!this.fields) {
30443             var autoId = 0;
30444             this.fields = new Roo.util.MixedCollection(false, function(o){
30445                 return o.id || ("item" + (++autoId));
30446             });
30447
30448         }
30449         
30450         var td = this.nextBlock();
30451         field.render(td);
30452         var ti = new Roo.Toolbar.Item(td.firstChild);
30453         ti.render(td);
30454         this.items.add(ti);
30455         this.fields.add(field);
30456         return ti;
30457     },
30458     /**
30459      * Hide the toolbar
30460      * @method hide
30461      */
30462      
30463       
30464     hide : function()
30465     {
30466         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30467         this.el.child('div').hide();
30468     },
30469     /**
30470      * Show the toolbar
30471      * @method show
30472      */
30473     show : function()
30474     {
30475         this.el.child('div').show();
30476     },
30477       
30478     // private
30479     nextBlock : function(){
30480         var td = document.createElement("td");
30481         this.tr.appendChild(td);
30482         return td;
30483     },
30484
30485     // private
30486     destroy : function(){
30487         if(this.items){ // rendered?
30488             Roo.destroy.apply(Roo, this.items.items);
30489         }
30490         if(this.fields){ // rendered?
30491             Roo.destroy.apply(Roo, this.fields.items);
30492         }
30493         Roo.Element.uncache(this.el, this.tr);
30494     }
30495 };
30496
30497 /**
30498  * @class Roo.Toolbar.Item
30499  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30500  * @constructor
30501  * Creates a new Item
30502  * @param {HTMLElement} el 
30503  */
30504 Roo.Toolbar.Item = function(el){
30505     var cfg = {};
30506     if (typeof (el.xtype) != 'undefined') {
30507         cfg = el;
30508         el = cfg.el;
30509     }
30510     
30511     this.el = Roo.getDom(el);
30512     this.id = Roo.id(this.el);
30513     this.hidden = false;
30514     
30515     this.addEvents({
30516          /**
30517              * @event render
30518              * Fires when the button is rendered
30519              * @param {Button} this
30520              */
30521         'render': true
30522     });
30523     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30524 };
30525 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30526 //Roo.Toolbar.Item.prototype = {
30527     
30528     /**
30529      * Get this item's HTML Element
30530      * @return {HTMLElement}
30531      */
30532     getEl : function(){
30533        return this.el;  
30534     },
30535
30536     // private
30537     render : function(td){
30538         
30539          this.td = td;
30540         td.appendChild(this.el);
30541         
30542         this.fireEvent('render', this);
30543     },
30544     
30545     /**
30546      * Removes and destroys this item.
30547      */
30548     destroy : function(){
30549         this.td.parentNode.removeChild(this.td);
30550     },
30551     
30552     /**
30553      * Shows this item.
30554      */
30555     show: function(){
30556         this.hidden = false;
30557         this.td.style.display = "";
30558     },
30559     
30560     /**
30561      * Hides this item.
30562      */
30563     hide: function(){
30564         this.hidden = true;
30565         this.td.style.display = "none";
30566     },
30567     
30568     /**
30569      * Convenience function for boolean show/hide.
30570      * @param {Boolean} visible true to show/false to hide
30571      */
30572     setVisible: function(visible){
30573         if(visible) {
30574             this.show();
30575         }else{
30576             this.hide();
30577         }
30578     },
30579     
30580     /**
30581      * Try to focus this item.
30582      */
30583     focus : function(){
30584         Roo.fly(this.el).focus();
30585     },
30586     
30587     /**
30588      * Disables this item.
30589      */
30590     disable : function(){
30591         Roo.fly(this.td).addClass("x-item-disabled");
30592         this.disabled = true;
30593         this.el.disabled = true;
30594     },
30595     
30596     /**
30597      * Enables this item.
30598      */
30599     enable : function(){
30600         Roo.fly(this.td).removeClass("x-item-disabled");
30601         this.disabled = false;
30602         this.el.disabled = false;
30603     }
30604 });
30605
30606
30607 /**
30608  * @class Roo.Toolbar.Separator
30609  * @extends Roo.Toolbar.Item
30610  * A simple toolbar separator class
30611  * @constructor
30612  * Creates a new Separator
30613  */
30614 Roo.Toolbar.Separator = function(cfg){
30615     
30616     var s = document.createElement("span");
30617     s.className = "ytb-sep";
30618     if (cfg) {
30619         cfg.el = s;
30620     }
30621     
30622     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30623 };
30624 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30625     enable:Roo.emptyFn,
30626     disable:Roo.emptyFn,
30627     focus:Roo.emptyFn
30628 });
30629
30630 /**
30631  * @class Roo.Toolbar.Spacer
30632  * @extends Roo.Toolbar.Item
30633  * A simple element that adds extra horizontal space to a toolbar.
30634  * @constructor
30635  * Creates a new Spacer
30636  */
30637 Roo.Toolbar.Spacer = function(cfg){
30638     var s = document.createElement("div");
30639     s.className = "ytb-spacer";
30640     if (cfg) {
30641         cfg.el = s;
30642     }
30643     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30644 };
30645 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30646     enable:Roo.emptyFn,
30647     disable:Roo.emptyFn,
30648     focus:Roo.emptyFn
30649 });
30650
30651 /**
30652  * @class Roo.Toolbar.Fill
30653  * @extends Roo.Toolbar.Spacer
30654  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30655  * @constructor
30656  * Creates a new Spacer
30657  */
30658 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30659     // private
30660     render : function(td){
30661         td.style.width = '100%';
30662         Roo.Toolbar.Fill.superclass.render.call(this, td);
30663     }
30664 });
30665
30666 /**
30667  * @class Roo.Toolbar.TextItem
30668  * @extends Roo.Toolbar.Item
30669  * A simple class that renders text directly into a toolbar.
30670  * @constructor
30671  * Creates a new TextItem
30672  * @cfg {string} text 
30673  */
30674 Roo.Toolbar.TextItem = function(cfg){
30675     var  text = cfg || "";
30676     if (typeof(cfg) == 'object') {
30677         text = cfg.text || "";
30678     }  else {
30679         cfg = null;
30680     }
30681     var s = document.createElement("span");
30682     s.className = "ytb-text";
30683     s.innerHTML = text;
30684     if (cfg) {
30685         cfg.el  = s;
30686     }
30687     
30688     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30689 };
30690 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30691     
30692      
30693     enable:Roo.emptyFn,
30694     disable:Roo.emptyFn,
30695     focus:Roo.emptyFn
30696 });
30697
30698 /**
30699  * @class Roo.Toolbar.Button
30700  * @extends Roo.Button
30701  * A button that renders into a toolbar.
30702  * @constructor
30703  * Creates a new Button
30704  * @param {Object} config A standard {@link Roo.Button} config object
30705  */
30706 Roo.Toolbar.Button = function(config){
30707     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30708 };
30709 Roo.extend(Roo.Toolbar.Button, Roo.Button,
30710 {
30711     
30712     
30713     render : function(td){
30714         this.td = td;
30715         Roo.Toolbar.Button.superclass.render.call(this, td);
30716     },
30717     
30718     /**
30719      * Removes and destroys this button
30720      */
30721     destroy : function(){
30722         Roo.Toolbar.Button.superclass.destroy.call(this);
30723         this.td.parentNode.removeChild(this.td);
30724     },
30725     
30726     /**
30727      * Shows this button
30728      */
30729     show: function(){
30730         this.hidden = false;
30731         this.td.style.display = "";
30732     },
30733     
30734     /**
30735      * Hides this button
30736      */
30737     hide: function(){
30738         this.hidden = true;
30739         this.td.style.display = "none";
30740     },
30741
30742     /**
30743      * Disables this item
30744      */
30745     disable : function(){
30746         Roo.fly(this.td).addClass("x-item-disabled");
30747         this.disabled = true;
30748     },
30749
30750     /**
30751      * Enables this item
30752      */
30753     enable : function(){
30754         Roo.fly(this.td).removeClass("x-item-disabled");
30755         this.disabled = false;
30756     }
30757 });
30758 // backwards compat
30759 Roo.ToolbarButton = Roo.Toolbar.Button;
30760
30761 /**
30762  * @class Roo.Toolbar.SplitButton
30763  * @extends Roo.SplitButton
30764  * A menu button that renders into a toolbar.
30765  * @constructor
30766  * Creates a new SplitButton
30767  * @param {Object} config A standard {@link Roo.SplitButton} config object
30768  */
30769 Roo.Toolbar.SplitButton = function(config){
30770     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30771 };
30772 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30773     render : function(td){
30774         this.td = td;
30775         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30776     },
30777     
30778     /**
30779      * Removes and destroys this button
30780      */
30781     destroy : function(){
30782         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30783         this.td.parentNode.removeChild(this.td);
30784     },
30785     
30786     /**
30787      * Shows this button
30788      */
30789     show: function(){
30790         this.hidden = false;
30791         this.td.style.display = "";
30792     },
30793     
30794     /**
30795      * Hides this button
30796      */
30797     hide: function(){
30798         this.hidden = true;
30799         this.td.style.display = "none";
30800     }
30801 });
30802
30803 // backwards compat
30804 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30805  * Based on:
30806  * Ext JS Library 1.1.1
30807  * Copyright(c) 2006-2007, Ext JS, LLC.
30808  *
30809  * Originally Released Under LGPL - original licence link has changed is not relivant.
30810  *
30811  * Fork - LGPL
30812  * <script type="text/javascript">
30813  */
30814  
30815 /**
30816  * @class Roo.PagingToolbar
30817  * @extends Roo.Toolbar
30818  * @children   Roo.Toolbar.Item Roo.form.Field
30819  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30820  * @constructor
30821  * Create a new PagingToolbar
30822  * @param {Object} config The config object
30823  */
30824 Roo.PagingToolbar = function(el, ds, config)
30825 {
30826     // old args format still supported... - xtype is prefered..
30827     if (typeof(el) == 'object' && el.xtype) {
30828         // created from xtype...
30829         config = el;
30830         ds = el.dataSource;
30831         el = config.container;
30832     }
30833     var items = [];
30834     if (config.items) {
30835         items = config.items;
30836         config.items = [];
30837     }
30838     
30839     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30840     this.ds = ds;
30841     this.cursor = 0;
30842     this.renderButtons(this.el);
30843     this.bind(ds);
30844     
30845     // supprot items array.
30846    
30847     Roo.each(items, function(e) {
30848         this.add(Roo.factory(e));
30849     },this);
30850     
30851 };
30852
30853 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30854     /**
30855      * @cfg {Roo.data.Store} dataSource
30856      * The underlying data store providing the paged data
30857      */
30858     /**
30859      * @cfg {String/HTMLElement/Element} container
30860      * container The id or element that will contain the toolbar
30861      */
30862     /**
30863      * @cfg {Boolean} displayInfo
30864      * True to display the displayMsg (defaults to false)
30865      */
30866     /**
30867      * @cfg {Number} pageSize
30868      * The number of records to display per page (defaults to 20)
30869      */
30870     pageSize: 20,
30871     /**
30872      * @cfg {String} displayMsg
30873      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30874      */
30875     displayMsg : 'Displaying {0} - {1} of {2}',
30876     /**
30877      * @cfg {String} emptyMsg
30878      * The message to display when no records are found (defaults to "No data to display")
30879      */
30880     emptyMsg : 'No data to display',
30881     /**
30882      * Customizable piece of the default paging text (defaults to "Page")
30883      * @type String
30884      */
30885     beforePageText : "Page",
30886     /**
30887      * Customizable piece of the default paging text (defaults to "of %0")
30888      * @type String
30889      */
30890     afterPageText : "of {0}",
30891     /**
30892      * Customizable piece of the default paging text (defaults to "First Page")
30893      * @type String
30894      */
30895     firstText : "First Page",
30896     /**
30897      * Customizable piece of the default paging text (defaults to "Previous Page")
30898      * @type String
30899      */
30900     prevText : "Previous Page",
30901     /**
30902      * Customizable piece of the default paging text (defaults to "Next Page")
30903      * @type String
30904      */
30905     nextText : "Next Page",
30906     /**
30907      * Customizable piece of the default paging text (defaults to "Last Page")
30908      * @type String
30909      */
30910     lastText : "Last Page",
30911     /**
30912      * Customizable piece of the default paging text (defaults to "Refresh")
30913      * @type String
30914      */
30915     refreshText : "Refresh",
30916
30917     // private
30918     renderButtons : function(el){
30919         Roo.PagingToolbar.superclass.render.call(this, el);
30920         this.first = this.addButton({
30921             tooltip: this.firstText,
30922             cls: "x-btn-icon x-grid-page-first",
30923             disabled: true,
30924             handler: this.onClick.createDelegate(this, ["first"])
30925         });
30926         this.prev = this.addButton({
30927             tooltip: this.prevText,
30928             cls: "x-btn-icon x-grid-page-prev",
30929             disabled: true,
30930             handler: this.onClick.createDelegate(this, ["prev"])
30931         });
30932         //this.addSeparator();
30933         this.add(this.beforePageText);
30934         this.field = Roo.get(this.addDom({
30935            tag: "input",
30936            type: "text",
30937            size: "3",
30938            value: "1",
30939            cls: "x-grid-page-number"
30940         }).el);
30941         this.field.on("keydown", this.onPagingKeydown, this);
30942         this.field.on("focus", function(){this.dom.select();});
30943         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30944         this.field.setHeight(18);
30945         //this.addSeparator();
30946         this.next = this.addButton({
30947             tooltip: this.nextText,
30948             cls: "x-btn-icon x-grid-page-next",
30949             disabled: true,
30950             handler: this.onClick.createDelegate(this, ["next"])
30951         });
30952         this.last = this.addButton({
30953             tooltip: this.lastText,
30954             cls: "x-btn-icon x-grid-page-last",
30955             disabled: true,
30956             handler: this.onClick.createDelegate(this, ["last"])
30957         });
30958         //this.addSeparator();
30959         this.loading = this.addButton({
30960             tooltip: this.refreshText,
30961             cls: "x-btn-icon x-grid-loading",
30962             handler: this.onClick.createDelegate(this, ["refresh"])
30963         });
30964
30965         if(this.displayInfo){
30966             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30967         }
30968     },
30969
30970     // private
30971     updateInfo : function(){
30972         if(this.displayEl){
30973             var count = this.ds.getCount();
30974             var msg = count == 0 ?
30975                 this.emptyMsg :
30976                 String.format(
30977                     this.displayMsg,
30978                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30979                 );
30980             this.displayEl.update(msg);
30981         }
30982     },
30983
30984     // private
30985     onLoad : function(ds, r, o){
30986        this.cursor = o.params ? o.params.start : 0;
30987        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30988
30989        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30990        this.field.dom.value = ap;
30991        this.first.setDisabled(ap == 1);
30992        this.prev.setDisabled(ap == 1);
30993        this.next.setDisabled(ap == ps);
30994        this.last.setDisabled(ap == ps);
30995        this.loading.enable();
30996        this.updateInfo();
30997     },
30998
30999     // private
31000     getPageData : function(){
31001         var total = this.ds.getTotalCount();
31002         return {
31003             total : total,
31004             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31005             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31006         };
31007     },
31008
31009     // private
31010     onLoadError : function(){
31011         this.loading.enable();
31012     },
31013
31014     // private
31015     onPagingKeydown : function(e){
31016         var k = e.getKey();
31017         var d = this.getPageData();
31018         if(k == e.RETURN){
31019             var v = this.field.dom.value, pageNum;
31020             if(!v || isNaN(pageNum = parseInt(v, 10))){
31021                 this.field.dom.value = d.activePage;
31022                 return;
31023             }
31024             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31025             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31026             e.stopEvent();
31027         }
31028         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))
31029         {
31030           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31031           this.field.dom.value = pageNum;
31032           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31033           e.stopEvent();
31034         }
31035         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31036         {
31037           var v = this.field.dom.value, pageNum; 
31038           var increment = (e.shiftKey) ? 10 : 1;
31039           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31040             increment *= -1;
31041           }
31042           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31043             this.field.dom.value = d.activePage;
31044             return;
31045           }
31046           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31047           {
31048             this.field.dom.value = parseInt(v, 10) + increment;
31049             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31050             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31051           }
31052           e.stopEvent();
31053         }
31054     },
31055
31056     // private
31057     beforeLoad : function(){
31058         if(this.loading){
31059             this.loading.disable();
31060         }
31061     },
31062
31063     // private
31064     onClick : function(which){
31065         var ds = this.ds;
31066         switch(which){
31067             case "first":
31068                 ds.load({params:{start: 0, limit: this.pageSize}});
31069             break;
31070             case "prev":
31071                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31072             break;
31073             case "next":
31074                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31075             break;
31076             case "last":
31077                 var total = ds.getTotalCount();
31078                 var extra = total % this.pageSize;
31079                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31080                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31081             break;
31082             case "refresh":
31083                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31084             break;
31085         }
31086     },
31087
31088     /**
31089      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31090      * @param {Roo.data.Store} store The data store to unbind
31091      */
31092     unbind : function(ds){
31093         ds.un("beforeload", this.beforeLoad, this);
31094         ds.un("load", this.onLoad, this);
31095         ds.un("loadexception", this.onLoadError, this);
31096         ds.un("remove", this.updateInfo, this);
31097         ds.un("add", this.updateInfo, this);
31098         this.ds = undefined;
31099     },
31100
31101     /**
31102      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31103      * @param {Roo.data.Store} store The data store to bind
31104      */
31105     bind : function(ds){
31106         ds.on("beforeload", this.beforeLoad, this);
31107         ds.on("load", this.onLoad, this);
31108         ds.on("loadexception", this.onLoadError, this);
31109         ds.on("remove", this.updateInfo, this);
31110         ds.on("add", this.updateInfo, this);
31111         this.ds = ds;
31112     }
31113 });/*
31114  * Based on:
31115  * Ext JS Library 1.1.1
31116  * Copyright(c) 2006-2007, Ext JS, LLC.
31117  *
31118  * Originally Released Under LGPL - original licence link has changed is not relivant.
31119  *
31120  * Fork - LGPL
31121  * <script type="text/javascript">
31122  */
31123
31124 /**
31125  * @class Roo.Resizable
31126  * @extends Roo.util.Observable
31127  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31128  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31129  * 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
31130  * the element will be wrapped for you automatically.</p>
31131  * <p>Here is the list of valid resize handles:</p>
31132  * <pre>
31133 Value   Description
31134 ------  -------------------
31135  'n'     north
31136  's'     south
31137  'e'     east
31138  'w'     west
31139  'nw'    northwest
31140  'sw'    southwest
31141  'se'    southeast
31142  'ne'    northeast
31143  'hd'    horizontal drag
31144  'all'   all
31145 </pre>
31146  * <p>Here's an example showing the creation of a typical Resizable:</p>
31147  * <pre><code>
31148 var resizer = new Roo.Resizable("element-id", {
31149     handles: 'all',
31150     minWidth: 200,
31151     minHeight: 100,
31152     maxWidth: 500,
31153     maxHeight: 400,
31154     pinned: true
31155 });
31156 resizer.on("resize", myHandler);
31157 </code></pre>
31158  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31159  * resizer.east.setDisplayed(false);</p>
31160  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31161  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31162  * resize operation's new size (defaults to [0, 0])
31163  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31164  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31165  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31166  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31167  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31168  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31169  * @cfg {Number} width The width of the element in pixels (defaults to null)
31170  * @cfg {Number} height The height of the element in pixels (defaults to null)
31171  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31172  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31173  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31174  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31175  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31176  * in favor of the handles config option (defaults to false)
31177  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31178  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31179  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31180  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31181  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31182  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31183  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31184  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31185  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31186  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31187  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31188  * @constructor
31189  * Create a new resizable component
31190  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31191  * @param {Object} config configuration options
31192   */
31193 Roo.Resizable = function(el, config)
31194 {
31195     this.el = Roo.get(el);
31196
31197     if(config && config.wrap){
31198         config.resizeChild = this.el;
31199         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31200         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31201         this.el.setStyle("overflow", "hidden");
31202         this.el.setPositioning(config.resizeChild.getPositioning());
31203         config.resizeChild.clearPositioning();
31204         if(!config.width || !config.height){
31205             var csize = config.resizeChild.getSize();
31206             this.el.setSize(csize.width, csize.height);
31207         }
31208         if(config.pinned && !config.adjustments){
31209             config.adjustments = "auto";
31210         }
31211     }
31212
31213     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31214     this.proxy.unselectable();
31215     this.proxy.enableDisplayMode('block');
31216
31217     Roo.apply(this, config);
31218
31219     if(this.pinned){
31220         this.disableTrackOver = true;
31221         this.el.addClass("x-resizable-pinned");
31222     }
31223     // if the element isn't positioned, make it relative
31224     var position = this.el.getStyle("position");
31225     if(position != "absolute" && position != "fixed"){
31226         this.el.setStyle("position", "relative");
31227     }
31228     if(!this.handles){ // no handles passed, must be legacy style
31229         this.handles = 's,e,se';
31230         if(this.multiDirectional){
31231             this.handles += ',n,w';
31232         }
31233     }
31234     if(this.handles == "all"){
31235         this.handles = "n s e w ne nw se sw";
31236     }
31237     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31238     var ps = Roo.Resizable.positions;
31239     for(var i = 0, len = hs.length; i < len; i++){
31240         if(hs[i] && ps[hs[i]]){
31241             var pos = ps[hs[i]];
31242             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31243         }
31244     }
31245     // legacy
31246     this.corner = this.southeast;
31247     
31248     // updateBox = the box can move..
31249     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31250         this.updateBox = true;
31251     }
31252
31253     this.activeHandle = null;
31254
31255     if(this.resizeChild){
31256         if(typeof this.resizeChild == "boolean"){
31257             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31258         }else{
31259             this.resizeChild = Roo.get(this.resizeChild, true);
31260         }
31261     }
31262     
31263     if(this.adjustments == "auto"){
31264         var rc = this.resizeChild;
31265         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31266         if(rc && (hw || hn)){
31267             rc.position("relative");
31268             rc.setLeft(hw ? hw.el.getWidth() : 0);
31269             rc.setTop(hn ? hn.el.getHeight() : 0);
31270         }
31271         this.adjustments = [
31272             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31273             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31274         ];
31275     }
31276
31277     if(this.draggable){
31278         this.dd = this.dynamic ?
31279             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31280         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31281     }
31282
31283     // public events
31284     this.addEvents({
31285         /**
31286          * @event beforeresize
31287          * Fired before resize is allowed. Set enabled to false to cancel resize.
31288          * @param {Roo.Resizable} this
31289          * @param {Roo.EventObject} e The mousedown event
31290          */
31291         "beforeresize" : true,
31292         /**
31293          * @event resizing
31294          * Fired a resizing.
31295          * @param {Roo.Resizable} this
31296          * @param {Number} x The new x position
31297          * @param {Number} y The new y position
31298          * @param {Number} w The new w width
31299          * @param {Number} h The new h hight
31300          * @param {Roo.EventObject} e The mouseup event
31301          */
31302         "resizing" : true,
31303         /**
31304          * @event resize
31305          * Fired after a resize.
31306          * @param {Roo.Resizable} this
31307          * @param {Number} width The new width
31308          * @param {Number} height The new height
31309          * @param {Roo.EventObject} e The mouseup event
31310          */
31311         "resize" : true
31312     });
31313
31314     if(this.width !== null && this.height !== null){
31315         this.resizeTo(this.width, this.height);
31316     }else{
31317         this.updateChildSize();
31318     }
31319     if(Roo.isIE){
31320         this.el.dom.style.zoom = 1;
31321     }
31322     Roo.Resizable.superclass.constructor.call(this);
31323 };
31324
31325 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31326         resizeChild : false,
31327         adjustments : [0, 0],
31328         minWidth : 5,
31329         minHeight : 5,
31330         maxWidth : 10000,
31331         maxHeight : 10000,
31332         enabled : true,
31333         animate : false,
31334         duration : .35,
31335         dynamic : false,
31336         handles : false,
31337         multiDirectional : false,
31338         disableTrackOver : false,
31339         easing : 'easeOutStrong',
31340         widthIncrement : 0,
31341         heightIncrement : 0,
31342         pinned : false,
31343         width : null,
31344         height : null,
31345         preserveRatio : false,
31346         transparent: false,
31347         minX: 0,
31348         minY: 0,
31349         draggable: false,
31350
31351         /**
31352          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31353          */
31354         constrainTo: undefined,
31355         /**
31356          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31357          */
31358         resizeRegion: undefined,
31359
31360
31361     /**
31362      * Perform a manual resize
31363      * @param {Number} width
31364      * @param {Number} height
31365      */
31366     resizeTo : function(width, height){
31367         this.el.setSize(width, height);
31368         this.updateChildSize();
31369         this.fireEvent("resize", this, width, height, null);
31370     },
31371
31372     // private
31373     startSizing : function(e, handle){
31374         this.fireEvent("beforeresize", this, e);
31375         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31376
31377             if(!this.overlay){
31378                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31379                 this.overlay.unselectable();
31380                 this.overlay.enableDisplayMode("block");
31381                 this.overlay.on("mousemove", this.onMouseMove, this);
31382                 this.overlay.on("mouseup", this.onMouseUp, this);
31383             }
31384             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31385
31386             this.resizing = true;
31387             this.startBox = this.el.getBox();
31388             this.startPoint = e.getXY();
31389             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31390                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31391
31392             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31393             this.overlay.show();
31394
31395             if(this.constrainTo) {
31396                 var ct = Roo.get(this.constrainTo);
31397                 this.resizeRegion = ct.getRegion().adjust(
31398                     ct.getFrameWidth('t'),
31399                     ct.getFrameWidth('l'),
31400                     -ct.getFrameWidth('b'),
31401                     -ct.getFrameWidth('r')
31402                 );
31403             }
31404
31405             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31406             this.proxy.show();
31407             this.proxy.setBox(this.startBox);
31408             if(!this.dynamic){
31409                 this.proxy.setStyle('visibility', 'visible');
31410             }
31411         }
31412     },
31413
31414     // private
31415     onMouseDown : function(handle, e){
31416         if(this.enabled){
31417             e.stopEvent();
31418             this.activeHandle = handle;
31419             this.startSizing(e, handle);
31420         }
31421     },
31422
31423     // private
31424     onMouseUp : function(e){
31425         var size = this.resizeElement();
31426         this.resizing = false;
31427         this.handleOut();
31428         this.overlay.hide();
31429         this.proxy.hide();
31430         this.fireEvent("resize", this, size.width, size.height, e);
31431     },
31432
31433     // private
31434     updateChildSize : function(){
31435         
31436         if(this.resizeChild){
31437             var el = this.el;
31438             var child = this.resizeChild;
31439             var adj = this.adjustments;
31440             if(el.dom.offsetWidth){
31441                 var b = el.getSize(true);
31442                 child.setSize(b.width+adj[0], b.height+adj[1]);
31443             }
31444             // Second call here for IE
31445             // The first call enables instant resizing and
31446             // the second call corrects scroll bars if they
31447             // exist
31448             if(Roo.isIE){
31449                 setTimeout(function(){
31450                     if(el.dom.offsetWidth){
31451                         var b = el.getSize(true);
31452                         child.setSize(b.width+adj[0], b.height+adj[1]);
31453                     }
31454                 }, 10);
31455             }
31456         }
31457     },
31458
31459     // private
31460     snap : function(value, inc, min){
31461         if(!inc || !value) {
31462             return value;
31463         }
31464         var newValue = value;
31465         var m = value % inc;
31466         if(m > 0){
31467             if(m > (inc/2)){
31468                 newValue = value + (inc-m);
31469             }else{
31470                 newValue = value - m;
31471             }
31472         }
31473         return Math.max(min, newValue);
31474     },
31475
31476     // private
31477     resizeElement : function(){
31478         var box = this.proxy.getBox();
31479         if(this.updateBox){
31480             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31481         }else{
31482             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31483         }
31484         this.updateChildSize();
31485         if(!this.dynamic){
31486             this.proxy.hide();
31487         }
31488         return box;
31489     },
31490
31491     // private
31492     constrain : function(v, diff, m, mx){
31493         if(v - diff < m){
31494             diff = v - m;
31495         }else if(v - diff > mx){
31496             diff = mx - v;
31497         }
31498         return diff;
31499     },
31500
31501     // private
31502     onMouseMove : function(e){
31503         
31504         if(this.enabled){
31505             try{// try catch so if something goes wrong the user doesn't get hung
31506
31507             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31508                 return;
31509             }
31510
31511             //var curXY = this.startPoint;
31512             var curSize = this.curSize || this.startBox;
31513             var x = this.startBox.x, y = this.startBox.y;
31514             var ox = x, oy = y;
31515             var w = curSize.width, h = curSize.height;
31516             var ow = w, oh = h;
31517             var mw = this.minWidth, mh = this.minHeight;
31518             var mxw = this.maxWidth, mxh = this.maxHeight;
31519             var wi = this.widthIncrement;
31520             var hi = this.heightIncrement;
31521
31522             var eventXY = e.getXY();
31523             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31524             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31525
31526             var pos = this.activeHandle.position;
31527
31528             switch(pos){
31529                 case "east":
31530                     w += diffX;
31531                     w = Math.min(Math.max(mw, w), mxw);
31532                     break;
31533              
31534                 case "south":
31535                     h += diffY;
31536                     h = Math.min(Math.max(mh, h), mxh);
31537                     break;
31538                 case "southeast":
31539                     w += diffX;
31540                     h += diffY;
31541                     w = Math.min(Math.max(mw, w), mxw);
31542                     h = Math.min(Math.max(mh, h), mxh);
31543                     break;
31544                 case "north":
31545                     diffY = this.constrain(h, diffY, mh, mxh);
31546                     y += diffY;
31547                     h -= diffY;
31548                     break;
31549                 case "hdrag":
31550                     
31551                     if (wi) {
31552                         var adiffX = Math.abs(diffX);
31553                         var sub = (adiffX % wi); // how much 
31554                         if (sub > (wi/2)) { // far enough to snap
31555                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31556                         } else {
31557                             // remove difference.. 
31558                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31559                         }
31560                     }
31561                     x += diffX;
31562                     x = Math.max(this.minX, x);
31563                     break;
31564                 case "west":
31565                     diffX = this.constrain(w, diffX, mw, mxw);
31566                     x += diffX;
31567                     w -= diffX;
31568                     break;
31569                 case "northeast":
31570                     w += diffX;
31571                     w = Math.min(Math.max(mw, w), mxw);
31572                     diffY = this.constrain(h, diffY, mh, mxh);
31573                     y += diffY;
31574                     h -= diffY;
31575                     break;
31576                 case "northwest":
31577                     diffX = this.constrain(w, diffX, mw, mxw);
31578                     diffY = this.constrain(h, diffY, mh, mxh);
31579                     y += diffY;
31580                     h -= diffY;
31581                     x += diffX;
31582                     w -= diffX;
31583                     break;
31584                case "southwest":
31585                     diffX = this.constrain(w, diffX, mw, mxw);
31586                     h += diffY;
31587                     h = Math.min(Math.max(mh, h), mxh);
31588                     x += diffX;
31589                     w -= diffX;
31590                     break;
31591             }
31592
31593             var sw = this.snap(w, wi, mw);
31594             var sh = this.snap(h, hi, mh);
31595             if(sw != w || sh != h){
31596                 switch(pos){
31597                     case "northeast":
31598                         y -= sh - h;
31599                     break;
31600                     case "north":
31601                         y -= sh - h;
31602                         break;
31603                     case "southwest":
31604                         x -= sw - w;
31605                     break;
31606                     case "west":
31607                         x -= sw - w;
31608                         break;
31609                     case "northwest":
31610                         x -= sw - w;
31611                         y -= sh - h;
31612                     break;
31613                 }
31614                 w = sw;
31615                 h = sh;
31616             }
31617
31618             if(this.preserveRatio){
31619                 switch(pos){
31620                     case "southeast":
31621                     case "east":
31622                         h = oh * (w/ow);
31623                         h = Math.min(Math.max(mh, h), mxh);
31624                         w = ow * (h/oh);
31625                        break;
31626                     case "south":
31627                         w = ow * (h/oh);
31628                         w = Math.min(Math.max(mw, w), mxw);
31629                         h = oh * (w/ow);
31630                         break;
31631                     case "northeast":
31632                         w = ow * (h/oh);
31633                         w = Math.min(Math.max(mw, w), mxw);
31634                         h = oh * (w/ow);
31635                     break;
31636                     case "north":
31637                         var tw = w;
31638                         w = ow * (h/oh);
31639                         w = Math.min(Math.max(mw, w), mxw);
31640                         h = oh * (w/ow);
31641                         x += (tw - w) / 2;
31642                         break;
31643                     case "southwest":
31644                         h = oh * (w/ow);
31645                         h = Math.min(Math.max(mh, h), mxh);
31646                         var tw = w;
31647                         w = ow * (h/oh);
31648                         x += tw - w;
31649                         break;
31650                     case "west":
31651                         var th = h;
31652                         h = oh * (w/ow);
31653                         h = Math.min(Math.max(mh, h), mxh);
31654                         y += (th - h) / 2;
31655                         var tw = w;
31656                         w = ow * (h/oh);
31657                         x += tw - w;
31658                        break;
31659                     case "northwest":
31660                         var tw = w;
31661                         var th = h;
31662                         h = oh * (w/ow);
31663                         h = Math.min(Math.max(mh, h), mxh);
31664                         w = ow * (h/oh);
31665                         y += th - h;
31666                         x += tw - w;
31667                        break;
31668
31669                 }
31670             }
31671             if (pos == 'hdrag') {
31672                 w = ow;
31673             }
31674             this.proxy.setBounds(x, y, w, h);
31675             if(this.dynamic){
31676                 this.resizeElement();
31677             }
31678             }catch(e){}
31679         }
31680         this.fireEvent("resizing", this, x, y, w, h, e);
31681     },
31682
31683     // private
31684     handleOver : function(){
31685         if(this.enabled){
31686             this.el.addClass("x-resizable-over");
31687         }
31688     },
31689
31690     // private
31691     handleOut : function(){
31692         if(!this.resizing){
31693             this.el.removeClass("x-resizable-over");
31694         }
31695     },
31696
31697     /**
31698      * Returns the element this component is bound to.
31699      * @return {Roo.Element}
31700      */
31701     getEl : function(){
31702         return this.el;
31703     },
31704
31705     /**
31706      * Returns the resizeChild element (or null).
31707      * @return {Roo.Element}
31708      */
31709     getResizeChild : function(){
31710         return this.resizeChild;
31711     },
31712     groupHandler : function()
31713     {
31714         
31715     },
31716     /**
31717      * Destroys this resizable. If the element was wrapped and
31718      * removeEl is not true then the element remains.
31719      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31720      */
31721     destroy : function(removeEl){
31722         this.proxy.remove();
31723         if(this.overlay){
31724             this.overlay.removeAllListeners();
31725             this.overlay.remove();
31726         }
31727         var ps = Roo.Resizable.positions;
31728         for(var k in ps){
31729             if(typeof ps[k] != "function" && this[ps[k]]){
31730                 var h = this[ps[k]];
31731                 h.el.removeAllListeners();
31732                 h.el.remove();
31733             }
31734         }
31735         if(removeEl){
31736             this.el.update("");
31737             this.el.remove();
31738         }
31739     }
31740 });
31741
31742 // private
31743 // hash to map config positions to true positions
31744 Roo.Resizable.positions = {
31745     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31746     hd: "hdrag"
31747 };
31748
31749 // private
31750 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31751     if(!this.tpl){
31752         // only initialize the template if resizable is used
31753         var tpl = Roo.DomHelper.createTemplate(
31754             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31755         );
31756         tpl.compile();
31757         Roo.Resizable.Handle.prototype.tpl = tpl;
31758     }
31759     this.position = pos;
31760     this.rz = rz;
31761     // show north drag fro topdra
31762     var handlepos = pos == 'hdrag' ? 'north' : pos;
31763     
31764     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31765     if (pos == 'hdrag') {
31766         this.el.setStyle('cursor', 'pointer');
31767     }
31768     this.el.unselectable();
31769     if(transparent){
31770         this.el.setOpacity(0);
31771     }
31772     this.el.on("mousedown", this.onMouseDown, this);
31773     if(!disableTrackOver){
31774         this.el.on("mouseover", this.onMouseOver, this);
31775         this.el.on("mouseout", this.onMouseOut, this);
31776     }
31777 };
31778
31779 // private
31780 Roo.Resizable.Handle.prototype = {
31781     afterResize : function(rz){
31782         Roo.log('after?');
31783         // do nothing
31784     },
31785     // private
31786     onMouseDown : function(e){
31787         this.rz.onMouseDown(this, e);
31788     },
31789     // private
31790     onMouseOver : function(e){
31791         this.rz.handleOver(this, e);
31792     },
31793     // private
31794     onMouseOut : function(e){
31795         this.rz.handleOut(this, e);
31796     }
31797 };/*
31798  * Based on:
31799  * Ext JS Library 1.1.1
31800  * Copyright(c) 2006-2007, Ext JS, LLC.
31801  *
31802  * Originally Released Under LGPL - original licence link has changed is not relivant.
31803  *
31804  * Fork - LGPL
31805  * <script type="text/javascript">
31806  */
31807
31808 /**
31809  * @class Roo.Editor
31810  * @extends Roo.Component
31811  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31812  * @constructor
31813  * Create a new Editor
31814  * @param {Roo.form.Field} field The Field object (or descendant)
31815  * @param {Object} config The config object
31816  */
31817 Roo.Editor = function(field, config){
31818     Roo.Editor.superclass.constructor.call(this, config);
31819     this.field = field;
31820     this.addEvents({
31821         /**
31822              * @event beforestartedit
31823              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31824              * false from the handler of this event.
31825              * @param {Editor} this
31826              * @param {Roo.Element} boundEl The underlying element bound to this editor
31827              * @param {Mixed} value The field value being set
31828              */
31829         "beforestartedit" : true,
31830         /**
31831              * @event startedit
31832              * Fires when this editor is displayed
31833              * @param {Roo.Element} boundEl The underlying element bound to this editor
31834              * @param {Mixed} value The starting field value
31835              */
31836         "startedit" : true,
31837         /**
31838              * @event beforecomplete
31839              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31840              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31841              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31842              * event will not fire since no edit actually occurred.
31843              * @param {Editor} this
31844              * @param {Mixed} value The current field value
31845              * @param {Mixed} startValue The original field value
31846              */
31847         "beforecomplete" : true,
31848         /**
31849              * @event complete
31850              * Fires after editing is complete and any changed value has been written to the underlying field.
31851              * @param {Editor} this
31852              * @param {Mixed} value The current field value
31853              * @param {Mixed} startValue The original field value
31854              */
31855         "complete" : true,
31856         /**
31857          * @event specialkey
31858          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31859          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31860          * @param {Roo.form.Field} this
31861          * @param {Roo.EventObject} e The event object
31862          */
31863         "specialkey" : true
31864     });
31865 };
31866
31867 Roo.extend(Roo.Editor, Roo.Component, {
31868     /**
31869      * @cfg {Boolean/String} autosize
31870      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31871      * or "height" to adopt the height only (defaults to false)
31872      */
31873     /**
31874      * @cfg {Boolean} revertInvalid
31875      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31876      * validation fails (defaults to true)
31877      */
31878     /**
31879      * @cfg {Boolean} ignoreNoChange
31880      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31881      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31882      * will never be ignored.
31883      */
31884     /**
31885      * @cfg {Boolean} hideEl
31886      * False to keep the bound element visible while the editor is displayed (defaults to true)
31887      */
31888     /**
31889      * @cfg {Mixed} value
31890      * The data value of the underlying field (defaults to "")
31891      */
31892     value : "",
31893     /**
31894      * @cfg {String} alignment
31895      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31896      */
31897     alignment: "c-c?",
31898     /**
31899      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31900      * for bottom-right shadow (defaults to "frame")
31901      */
31902     shadow : "frame",
31903     /**
31904      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31905      */
31906     constrain : false,
31907     /**
31908      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31909      */
31910     completeOnEnter : false,
31911     /**
31912      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31913      */
31914     cancelOnEsc : false,
31915     /**
31916      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31917      */
31918     updateEl : false,
31919
31920     // private
31921     onRender : function(ct, position){
31922         this.el = new Roo.Layer({
31923             shadow: this.shadow,
31924             cls: "x-editor",
31925             parentEl : ct,
31926             shim : this.shim,
31927             shadowOffset:4,
31928             id: this.id,
31929             constrain: this.constrain
31930         });
31931         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31932         if(this.field.msgTarget != 'title'){
31933             this.field.msgTarget = 'qtip';
31934         }
31935         this.field.render(this.el);
31936         if(Roo.isGecko){
31937             this.field.el.dom.setAttribute('autocomplete', 'off');
31938         }
31939         this.field.on("specialkey", this.onSpecialKey, this);
31940         if(this.swallowKeys){
31941             this.field.el.swallowEvent(['keydown','keypress']);
31942         }
31943         this.field.show();
31944         this.field.on("blur", this.onBlur, this);
31945         if(this.field.grow){
31946             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31947         }
31948     },
31949
31950     onSpecialKey : function(field, e)
31951     {
31952         //Roo.log('editor onSpecialKey');
31953         if(this.completeOnEnter && e.getKey() == e.ENTER){
31954             e.stopEvent();
31955             this.completeEdit();
31956             return;
31957         }
31958         // do not fire special key otherwise it might hide close the editor...
31959         if(e.getKey() == e.ENTER){    
31960             return;
31961         }
31962         if(this.cancelOnEsc && e.getKey() == e.ESC){
31963             this.cancelEdit();
31964             return;
31965         } 
31966         this.fireEvent('specialkey', field, e);
31967     
31968     },
31969
31970     /**
31971      * Starts the editing process and shows the editor.
31972      * @param {String/HTMLElement/Element} el The element to edit
31973      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31974       * to the innerHTML of el.
31975      */
31976     startEdit : function(el, value){
31977         if(this.editing){
31978             this.completeEdit();
31979         }
31980         this.boundEl = Roo.get(el);
31981         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31982         if(!this.rendered){
31983             this.render(this.parentEl || document.body);
31984         }
31985         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31986             return;
31987         }
31988         this.startValue = v;
31989         this.field.setValue(v);
31990         if(this.autoSize){
31991             var sz = this.boundEl.getSize();
31992             switch(this.autoSize){
31993                 case "width":
31994                 this.setSize(sz.width,  "");
31995                 break;
31996                 case "height":
31997                 this.setSize("",  sz.height);
31998                 break;
31999                 default:
32000                 this.setSize(sz.width,  sz.height);
32001             }
32002         }
32003         this.el.alignTo(this.boundEl, this.alignment);
32004         this.editing = true;
32005         if(Roo.QuickTips){
32006             Roo.QuickTips.disable();
32007         }
32008         this.show();
32009     },
32010
32011     /**
32012      * Sets the height and width of this editor.
32013      * @param {Number} width The new width
32014      * @param {Number} height The new height
32015      */
32016     setSize : function(w, h){
32017         this.field.setSize(w, h);
32018         if(this.el){
32019             this.el.sync();
32020         }
32021     },
32022
32023     /**
32024      * Realigns the editor to the bound field based on the current alignment config value.
32025      */
32026     realign : function(){
32027         this.el.alignTo(this.boundEl, this.alignment);
32028     },
32029
32030     /**
32031      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32032      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32033      */
32034     completeEdit : function(remainVisible){
32035         if(!this.editing){
32036             return;
32037         }
32038         var v = this.getValue();
32039         if(this.revertInvalid !== false && !this.field.isValid()){
32040             v = this.startValue;
32041             this.cancelEdit(true);
32042         }
32043         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32044             this.editing = false;
32045             this.hide();
32046             return;
32047         }
32048         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32049             this.editing = false;
32050             if(this.updateEl && this.boundEl){
32051                 this.boundEl.update(v);
32052             }
32053             if(remainVisible !== true){
32054                 this.hide();
32055             }
32056             this.fireEvent("complete", this, v, this.startValue);
32057         }
32058     },
32059
32060     // private
32061     onShow : function(){
32062         this.el.show();
32063         if(this.hideEl !== false){
32064             this.boundEl.hide();
32065         }
32066         this.field.show();
32067         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32068             this.fixIEFocus = true;
32069             this.deferredFocus.defer(50, this);
32070         }else{
32071             this.field.focus();
32072         }
32073         this.fireEvent("startedit", this.boundEl, this.startValue);
32074     },
32075
32076     deferredFocus : function(){
32077         if(this.editing){
32078             this.field.focus();
32079         }
32080     },
32081
32082     /**
32083      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32084      * reverted to the original starting value.
32085      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32086      * cancel (defaults to false)
32087      */
32088     cancelEdit : function(remainVisible){
32089         if(this.editing){
32090             this.setValue(this.startValue);
32091             if(remainVisible !== true){
32092                 this.hide();
32093             }
32094         }
32095     },
32096
32097     // private
32098     onBlur : function(){
32099         if(this.allowBlur !== true && this.editing){
32100             this.completeEdit();
32101         }
32102     },
32103
32104     // private
32105     onHide : function(){
32106         if(this.editing){
32107             this.completeEdit();
32108             return;
32109         }
32110         this.field.blur();
32111         if(this.field.collapse){
32112             this.field.collapse();
32113         }
32114         this.el.hide();
32115         if(this.hideEl !== false){
32116             this.boundEl.show();
32117         }
32118         if(Roo.QuickTips){
32119             Roo.QuickTips.enable();
32120         }
32121     },
32122
32123     /**
32124      * Sets the data value of the editor
32125      * @param {Mixed} value Any valid value supported by the underlying field
32126      */
32127     setValue : function(v){
32128         this.field.setValue(v);
32129     },
32130
32131     /**
32132      * Gets the data value of the editor
32133      * @return {Mixed} The data value
32134      */
32135     getValue : function(){
32136         return this.field.getValue();
32137     }
32138 });/*
32139  * Based on:
32140  * Ext JS Library 1.1.1
32141  * Copyright(c) 2006-2007, Ext JS, LLC.
32142  *
32143  * Originally Released Under LGPL - original licence link has changed is not relivant.
32144  *
32145  * Fork - LGPL
32146  * <script type="text/javascript">
32147  */
32148  
32149 /**
32150  * @class Roo.BasicDialog
32151  * @extends Roo.util.Observable
32152  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32153  * <pre><code>
32154 var dlg = new Roo.BasicDialog("my-dlg", {
32155     height: 200,
32156     width: 300,
32157     minHeight: 100,
32158     minWidth: 150,
32159     modal: true,
32160     proxyDrag: true,
32161     shadow: true
32162 });
32163 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32164 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32165 dlg.addButton('Cancel', dlg.hide, dlg);
32166 dlg.show();
32167 </code></pre>
32168   <b>A Dialog should always be a direct child of the body element.</b>
32169  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32170  * @cfg {String} title Default text to display in the title bar (defaults to null)
32171  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32172  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32173  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32174  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32175  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32176  * (defaults to null with no animation)
32177  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32178  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32179  * property for valid values (defaults to 'all')
32180  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32181  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32182  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32183  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32184  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32185  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32186  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32187  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32188  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32189  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32190  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32191  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32192  * draggable = true (defaults to false)
32193  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32194  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32195  * shadow (defaults to false)
32196  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32197  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32198  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32199  * @cfg {Array} buttons Array of buttons
32200  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32201  * @constructor
32202  * Create a new BasicDialog.
32203  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32204  * @param {Object} config Configuration options
32205  */
32206 Roo.BasicDialog = function(el, config){
32207     this.el = Roo.get(el);
32208     var dh = Roo.DomHelper;
32209     if(!this.el && config && config.autoCreate){
32210         if(typeof config.autoCreate == "object"){
32211             if(!config.autoCreate.id){
32212                 config.autoCreate.id = el;
32213             }
32214             this.el = dh.append(document.body,
32215                         config.autoCreate, true);
32216         }else{
32217             this.el = dh.append(document.body,
32218                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32219         }
32220     }
32221     el = this.el;
32222     el.setDisplayed(true);
32223     el.hide = this.hideAction;
32224     this.id = el.id;
32225     el.addClass("x-dlg");
32226
32227     Roo.apply(this, config);
32228
32229     this.proxy = el.createProxy("x-dlg-proxy");
32230     this.proxy.hide = this.hideAction;
32231     this.proxy.setOpacity(.5);
32232     this.proxy.hide();
32233
32234     if(config.width){
32235         el.setWidth(config.width);
32236     }
32237     if(config.height){
32238         el.setHeight(config.height);
32239     }
32240     this.size = el.getSize();
32241     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32242         this.xy = [config.x,config.y];
32243     }else{
32244         this.xy = el.getCenterXY(true);
32245     }
32246     /** The header element @type Roo.Element */
32247     this.header = el.child("> .x-dlg-hd");
32248     /** The body element @type Roo.Element */
32249     this.body = el.child("> .x-dlg-bd");
32250     /** The footer element @type Roo.Element */
32251     this.footer = el.child("> .x-dlg-ft");
32252
32253     if(!this.header){
32254         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32255     }
32256     if(!this.body){
32257         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32258     }
32259
32260     this.header.unselectable();
32261     if(this.title){
32262         this.header.update(this.title);
32263     }
32264     // this element allows the dialog to be focused for keyboard event
32265     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32266     this.focusEl.swallowEvent("click", true);
32267
32268     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32269
32270     // wrap the body and footer for special rendering
32271     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32272     if(this.footer){
32273         this.bwrap.dom.appendChild(this.footer.dom);
32274     }
32275
32276     this.bg = this.el.createChild({
32277         tag: "div", cls:"x-dlg-bg",
32278         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32279     });
32280     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32281
32282
32283     if(this.autoScroll !== false && !this.autoTabs){
32284         this.body.setStyle("overflow", "auto");
32285     }
32286
32287     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32288
32289     if(this.closable !== false){
32290         this.el.addClass("x-dlg-closable");
32291         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32292         this.close.on("click", this.closeClick, this);
32293         this.close.addClassOnOver("x-dlg-close-over");
32294     }
32295     if(this.collapsible !== false){
32296         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32297         this.collapseBtn.on("click", this.collapseClick, this);
32298         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32299         this.header.on("dblclick", this.collapseClick, this);
32300     }
32301     if(this.resizable !== false){
32302         this.el.addClass("x-dlg-resizable");
32303         this.resizer = new Roo.Resizable(el, {
32304             minWidth: this.minWidth || 80,
32305             minHeight:this.minHeight || 80,
32306             handles: this.resizeHandles || "all",
32307             pinned: true
32308         });
32309         this.resizer.on("beforeresize", this.beforeResize, this);
32310         this.resizer.on("resize", this.onResize, this);
32311     }
32312     if(this.draggable !== false){
32313         el.addClass("x-dlg-draggable");
32314         if (!this.proxyDrag) {
32315             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32316         }
32317         else {
32318             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32319         }
32320         dd.setHandleElId(this.header.id);
32321         dd.endDrag = this.endMove.createDelegate(this);
32322         dd.startDrag = this.startMove.createDelegate(this);
32323         dd.onDrag = this.onDrag.createDelegate(this);
32324         dd.scroll = false;
32325         this.dd = dd;
32326     }
32327     if(this.modal){
32328         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32329         this.mask.enableDisplayMode("block");
32330         this.mask.hide();
32331         this.el.addClass("x-dlg-modal");
32332     }
32333     if(this.shadow){
32334         this.shadow = new Roo.Shadow({
32335             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32336             offset : this.shadowOffset
32337         });
32338     }else{
32339         this.shadowOffset = 0;
32340     }
32341     if(Roo.useShims && this.shim !== false){
32342         this.shim = this.el.createShim();
32343         this.shim.hide = this.hideAction;
32344         this.shim.hide();
32345     }else{
32346         this.shim = false;
32347     }
32348     if(this.autoTabs){
32349         this.initTabs();
32350     }
32351     if (this.buttons) { 
32352         var bts= this.buttons;
32353         this.buttons = [];
32354         Roo.each(bts, function(b) {
32355             this.addButton(b);
32356         }, this);
32357     }
32358     
32359     
32360     this.addEvents({
32361         /**
32362          * @event keydown
32363          * Fires when a key is pressed
32364          * @param {Roo.BasicDialog} this
32365          * @param {Roo.EventObject} e
32366          */
32367         "keydown" : true,
32368         /**
32369          * @event move
32370          * Fires when this dialog is moved by the user.
32371          * @param {Roo.BasicDialog} this
32372          * @param {Number} x The new page X
32373          * @param {Number} y The new page Y
32374          */
32375         "move" : true,
32376         /**
32377          * @event resize
32378          * Fires when this dialog is resized by the user.
32379          * @param {Roo.BasicDialog} this
32380          * @param {Number} width The new width
32381          * @param {Number} height The new height
32382          */
32383         "resize" : true,
32384         /**
32385          * @event beforehide
32386          * Fires before this dialog is hidden.
32387          * @param {Roo.BasicDialog} this
32388          */
32389         "beforehide" : true,
32390         /**
32391          * @event hide
32392          * Fires when this dialog is hidden.
32393          * @param {Roo.BasicDialog} this
32394          */
32395         "hide" : true,
32396         /**
32397          * @event beforeshow
32398          * Fires before this dialog is shown.
32399          * @param {Roo.BasicDialog} this
32400          */
32401         "beforeshow" : true,
32402         /**
32403          * @event show
32404          * Fires when this dialog is shown.
32405          * @param {Roo.BasicDialog} this
32406          */
32407         "show" : true
32408     });
32409     el.on("keydown", this.onKeyDown, this);
32410     el.on("mousedown", this.toFront, this);
32411     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32412     this.el.hide();
32413     Roo.DialogManager.register(this);
32414     Roo.BasicDialog.superclass.constructor.call(this);
32415 };
32416
32417 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32418     shadowOffset: Roo.isIE ? 6 : 5,
32419     minHeight: 80,
32420     minWidth: 200,
32421     minButtonWidth: 75,
32422     defaultButton: null,
32423     buttonAlign: "right",
32424     tabTag: 'div',
32425     firstShow: true,
32426
32427     /**
32428      * Sets the dialog title text
32429      * @param {String} text The title text to display
32430      * @return {Roo.BasicDialog} this
32431      */
32432     setTitle : function(text){
32433         this.header.update(text);
32434         return this;
32435     },
32436
32437     // private
32438     closeClick : function(){
32439         this.hide();
32440     },
32441
32442     // private
32443     collapseClick : function(){
32444         this[this.collapsed ? "expand" : "collapse"]();
32445     },
32446
32447     /**
32448      * Collapses the dialog to its minimized state (only the title bar is visible).
32449      * Equivalent to the user clicking the collapse dialog button.
32450      */
32451     collapse : function(){
32452         if(!this.collapsed){
32453             this.collapsed = true;
32454             this.el.addClass("x-dlg-collapsed");
32455             this.restoreHeight = this.el.getHeight();
32456             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32457         }
32458     },
32459
32460     /**
32461      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32462      * clicking the expand dialog button.
32463      */
32464     expand : function(){
32465         if(this.collapsed){
32466             this.collapsed = false;
32467             this.el.removeClass("x-dlg-collapsed");
32468             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32469         }
32470     },
32471
32472     /**
32473      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32474      * @return {Roo.TabPanel} The tabs component
32475      */
32476     initTabs : function(){
32477         var tabs = this.getTabs();
32478         while(tabs.getTab(0)){
32479             tabs.removeTab(0);
32480         }
32481         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32482             var dom = el.dom;
32483             tabs.addTab(Roo.id(dom), dom.title);
32484             dom.title = "";
32485         });
32486         tabs.activate(0);
32487         return tabs;
32488     },
32489
32490     // private
32491     beforeResize : function(){
32492         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32493     },
32494
32495     // private
32496     onResize : function(){
32497         this.refreshSize();
32498         this.syncBodyHeight();
32499         this.adjustAssets();
32500         this.focus();
32501         this.fireEvent("resize", this, this.size.width, this.size.height);
32502     },
32503
32504     // private
32505     onKeyDown : function(e){
32506         if(this.isVisible()){
32507             this.fireEvent("keydown", this, e);
32508         }
32509     },
32510
32511     /**
32512      * Resizes the dialog.
32513      * @param {Number} width
32514      * @param {Number} height
32515      * @return {Roo.BasicDialog} this
32516      */
32517     resizeTo : function(width, height){
32518         this.el.setSize(width, height);
32519         this.size = {width: width, height: height};
32520         this.syncBodyHeight();
32521         if(this.fixedcenter){
32522             this.center();
32523         }
32524         if(this.isVisible()){
32525             this.constrainXY();
32526             this.adjustAssets();
32527         }
32528         this.fireEvent("resize", this, width, height);
32529         return this;
32530     },
32531
32532
32533     /**
32534      * Resizes the dialog to fit the specified content size.
32535      * @param {Number} width
32536      * @param {Number} height
32537      * @return {Roo.BasicDialog} this
32538      */
32539     setContentSize : function(w, h){
32540         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32541         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32542         //if(!this.el.isBorderBox()){
32543             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32544             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32545         //}
32546         if(this.tabs){
32547             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32548             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32549         }
32550         this.resizeTo(w, h);
32551         return this;
32552     },
32553
32554     /**
32555      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32556      * executed in response to a particular key being pressed while the dialog is active.
32557      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32558      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32559      * @param {Function} fn The function to call
32560      * @param {Object} scope (optional) The scope of the function
32561      * @return {Roo.BasicDialog} this
32562      */
32563     addKeyListener : function(key, fn, scope){
32564         var keyCode, shift, ctrl, alt;
32565         if(typeof key == "object" && !(key instanceof Array)){
32566             keyCode = key["key"];
32567             shift = key["shift"];
32568             ctrl = key["ctrl"];
32569             alt = key["alt"];
32570         }else{
32571             keyCode = key;
32572         }
32573         var handler = function(dlg, e){
32574             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32575                 var k = e.getKey();
32576                 if(keyCode instanceof Array){
32577                     for(var i = 0, len = keyCode.length; i < len; i++){
32578                         if(keyCode[i] == k){
32579                           fn.call(scope || window, dlg, k, e);
32580                           return;
32581                         }
32582                     }
32583                 }else{
32584                     if(k == keyCode){
32585                         fn.call(scope || window, dlg, k, e);
32586                     }
32587                 }
32588             }
32589         };
32590         this.on("keydown", handler);
32591         return this;
32592     },
32593
32594     /**
32595      * Returns the TabPanel component (creates it if it doesn't exist).
32596      * Note: If you wish to simply check for the existence of tabs without creating them,
32597      * check for a null 'tabs' property.
32598      * @return {Roo.TabPanel} The tabs component
32599      */
32600     getTabs : function(){
32601         if(!this.tabs){
32602             this.el.addClass("x-dlg-auto-tabs");
32603             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32604             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32605         }
32606         return this.tabs;
32607     },
32608
32609     /**
32610      * Adds a button to the footer section of the dialog.
32611      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32612      * object or a valid Roo.DomHelper element config
32613      * @param {Function} handler The function called when the button is clicked
32614      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32615      * @return {Roo.Button} The new button
32616      */
32617     addButton : function(config, handler, scope){
32618         var dh = Roo.DomHelper;
32619         if(!this.footer){
32620             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32621         }
32622         if(!this.btnContainer){
32623             var tb = this.footer.createChild({
32624
32625                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32626                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32627             }, null, true);
32628             this.btnContainer = tb.firstChild.firstChild.firstChild;
32629         }
32630         var bconfig = {
32631             handler: handler,
32632             scope: scope,
32633             minWidth: this.minButtonWidth,
32634             hideParent:true
32635         };
32636         if(typeof config == "string"){
32637             bconfig.text = config;
32638         }else{
32639             if(config.tag){
32640                 bconfig.dhconfig = config;
32641             }else{
32642                 Roo.apply(bconfig, config);
32643             }
32644         }
32645         var fc = false;
32646         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32647             bconfig.position = Math.max(0, bconfig.position);
32648             fc = this.btnContainer.childNodes[bconfig.position];
32649         }
32650          
32651         var btn = new Roo.Button(
32652             fc ? 
32653                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32654                 : this.btnContainer.appendChild(document.createElement("td")),
32655             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32656             bconfig
32657         );
32658         this.syncBodyHeight();
32659         if(!this.buttons){
32660             /**
32661              * Array of all the buttons that have been added to this dialog via addButton
32662              * @type Array
32663              */
32664             this.buttons = [];
32665         }
32666         this.buttons.push(btn);
32667         return btn;
32668     },
32669
32670     /**
32671      * Sets the default button to be focused when the dialog is displayed.
32672      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32673      * @return {Roo.BasicDialog} this
32674      */
32675     setDefaultButton : function(btn){
32676         this.defaultButton = btn;
32677         return this;
32678     },
32679
32680     // private
32681     getHeaderFooterHeight : function(safe){
32682         var height = 0;
32683         if(this.header){
32684            height += this.header.getHeight();
32685         }
32686         if(this.footer){
32687            var fm = this.footer.getMargins();
32688             height += (this.footer.getHeight()+fm.top+fm.bottom);
32689         }
32690         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32691         height += this.centerBg.getPadding("tb");
32692         return height;
32693     },
32694
32695     // private
32696     syncBodyHeight : function()
32697     {
32698         var bd = this.body, // the text
32699             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32700             bw = this.bwrap;
32701         var height = this.size.height - this.getHeaderFooterHeight(false);
32702         bd.setHeight(height-bd.getMargins("tb"));
32703         var hh = this.header.getHeight();
32704         var h = this.size.height-hh;
32705         cb.setHeight(h);
32706         
32707         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32708         bw.setHeight(h-cb.getPadding("tb"));
32709         
32710         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32711         bd.setWidth(bw.getWidth(true));
32712         if(this.tabs){
32713             this.tabs.syncHeight();
32714             if(Roo.isIE){
32715                 this.tabs.el.repaint();
32716             }
32717         }
32718     },
32719
32720     /**
32721      * Restores the previous state of the dialog if Roo.state is configured.
32722      * @return {Roo.BasicDialog} this
32723      */
32724     restoreState : function(){
32725         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32726         if(box && box.width){
32727             this.xy = [box.x, box.y];
32728             this.resizeTo(box.width, box.height);
32729         }
32730         return this;
32731     },
32732
32733     // private
32734     beforeShow : function(){
32735         this.expand();
32736         if(this.fixedcenter){
32737             this.xy = this.el.getCenterXY(true);
32738         }
32739         if(this.modal){
32740             Roo.get(document.body).addClass("x-body-masked");
32741             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32742             this.mask.show();
32743         }
32744         this.constrainXY();
32745     },
32746
32747     // private
32748     animShow : function(){
32749         var b = Roo.get(this.animateTarget).getBox();
32750         this.proxy.setSize(b.width, b.height);
32751         this.proxy.setLocation(b.x, b.y);
32752         this.proxy.show();
32753         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32754                     true, .35, this.showEl.createDelegate(this));
32755     },
32756
32757     /**
32758      * Shows the dialog.
32759      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32760      * @return {Roo.BasicDialog} this
32761      */
32762     show : function(animateTarget){
32763         if (this.fireEvent("beforeshow", this) === false){
32764             return;
32765         }
32766         if(this.syncHeightBeforeShow){
32767             this.syncBodyHeight();
32768         }else if(this.firstShow){
32769             this.firstShow = false;
32770             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32771         }
32772         this.animateTarget = animateTarget || this.animateTarget;
32773         if(!this.el.isVisible()){
32774             this.beforeShow();
32775             if(this.animateTarget && Roo.get(this.animateTarget)){
32776                 this.animShow();
32777             }else{
32778                 this.showEl();
32779             }
32780         }
32781         return this;
32782     },
32783
32784     // private
32785     showEl : function(){
32786         this.proxy.hide();
32787         this.el.setXY(this.xy);
32788         this.el.show();
32789         this.adjustAssets(true);
32790         this.toFront();
32791         this.focus();
32792         // IE peekaboo bug - fix found by Dave Fenwick
32793         if(Roo.isIE){
32794             this.el.repaint();
32795         }
32796         this.fireEvent("show", this);
32797     },
32798
32799     /**
32800      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32801      * dialog itself will receive focus.
32802      */
32803     focus : function(){
32804         if(this.defaultButton){
32805             this.defaultButton.focus();
32806         }else{
32807             this.focusEl.focus();
32808         }
32809     },
32810
32811     // private
32812     constrainXY : function(){
32813         if(this.constraintoviewport !== false){
32814             if(!this.viewSize){
32815                 if(this.container){
32816                     var s = this.container.getSize();
32817                     this.viewSize = [s.width, s.height];
32818                 }else{
32819                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32820                 }
32821             }
32822             var s = Roo.get(this.container||document).getScroll();
32823
32824             var x = this.xy[0], y = this.xy[1];
32825             var w = this.size.width, h = this.size.height;
32826             var vw = this.viewSize[0], vh = this.viewSize[1];
32827             // only move it if it needs it
32828             var moved = false;
32829             // first validate right/bottom
32830             if(x + w > vw+s.left){
32831                 x = vw - w;
32832                 moved = true;
32833             }
32834             if(y + h > vh+s.top){
32835                 y = vh - h;
32836                 moved = true;
32837             }
32838             // then make sure top/left isn't negative
32839             if(x < s.left){
32840                 x = s.left;
32841                 moved = true;
32842             }
32843             if(y < s.top){
32844                 y = s.top;
32845                 moved = true;
32846             }
32847             if(moved){
32848                 // cache xy
32849                 this.xy = [x, y];
32850                 if(this.isVisible()){
32851                     this.el.setLocation(x, y);
32852                     this.adjustAssets();
32853                 }
32854             }
32855         }
32856     },
32857
32858     // private
32859     onDrag : function(){
32860         if(!this.proxyDrag){
32861             this.xy = this.el.getXY();
32862             this.adjustAssets();
32863         }
32864     },
32865
32866     // private
32867     adjustAssets : function(doShow){
32868         var x = this.xy[0], y = this.xy[1];
32869         var w = this.size.width, h = this.size.height;
32870         if(doShow === true){
32871             if(this.shadow){
32872                 this.shadow.show(this.el);
32873             }
32874             if(this.shim){
32875                 this.shim.show();
32876             }
32877         }
32878         if(this.shadow && this.shadow.isVisible()){
32879             this.shadow.show(this.el);
32880         }
32881         if(this.shim && this.shim.isVisible()){
32882             this.shim.setBounds(x, y, w, h);
32883         }
32884     },
32885
32886     // private
32887     adjustViewport : function(w, h){
32888         if(!w || !h){
32889             w = Roo.lib.Dom.getViewWidth();
32890             h = Roo.lib.Dom.getViewHeight();
32891         }
32892         // cache the size
32893         this.viewSize = [w, h];
32894         if(this.modal && this.mask.isVisible()){
32895             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32896             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32897         }
32898         if(this.isVisible()){
32899             this.constrainXY();
32900         }
32901     },
32902
32903     /**
32904      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32905      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32906      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32907      */
32908     destroy : function(removeEl){
32909         if(this.isVisible()){
32910             this.animateTarget = null;
32911             this.hide();
32912         }
32913         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32914         if(this.tabs){
32915             this.tabs.destroy(removeEl);
32916         }
32917         Roo.destroy(
32918              this.shim,
32919              this.proxy,
32920              this.resizer,
32921              this.close,
32922              this.mask
32923         );
32924         if(this.dd){
32925             this.dd.unreg();
32926         }
32927         if(this.buttons){
32928            for(var i = 0, len = this.buttons.length; i < len; i++){
32929                this.buttons[i].destroy();
32930            }
32931         }
32932         this.el.removeAllListeners();
32933         if(removeEl === true){
32934             this.el.update("");
32935             this.el.remove();
32936         }
32937         Roo.DialogManager.unregister(this);
32938     },
32939
32940     // private
32941     startMove : function(){
32942         if(this.proxyDrag){
32943             this.proxy.show();
32944         }
32945         if(this.constraintoviewport !== false){
32946             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32947         }
32948     },
32949
32950     // private
32951     endMove : function(){
32952         if(!this.proxyDrag){
32953             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32954         }else{
32955             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32956             this.proxy.hide();
32957         }
32958         this.refreshSize();
32959         this.adjustAssets();
32960         this.focus();
32961         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32962     },
32963
32964     /**
32965      * Brings this dialog to the front of any other visible dialogs
32966      * @return {Roo.BasicDialog} this
32967      */
32968     toFront : function(){
32969         Roo.DialogManager.bringToFront(this);
32970         return this;
32971     },
32972
32973     /**
32974      * Sends this dialog to the back (under) of any other visible dialogs
32975      * @return {Roo.BasicDialog} this
32976      */
32977     toBack : function(){
32978         Roo.DialogManager.sendToBack(this);
32979         return this;
32980     },
32981
32982     /**
32983      * Centers this dialog in the viewport
32984      * @return {Roo.BasicDialog} this
32985      */
32986     center : function(){
32987         var xy = this.el.getCenterXY(true);
32988         this.moveTo(xy[0], xy[1]);
32989         return this;
32990     },
32991
32992     /**
32993      * Moves the dialog's top-left corner to the specified point
32994      * @param {Number} x
32995      * @param {Number} y
32996      * @return {Roo.BasicDialog} this
32997      */
32998     moveTo : function(x, y){
32999         this.xy = [x,y];
33000         if(this.isVisible()){
33001             this.el.setXY(this.xy);
33002             this.adjustAssets();
33003         }
33004         return this;
33005     },
33006
33007     /**
33008      * Aligns the dialog to the specified element
33009      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33010      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33011      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33012      * @return {Roo.BasicDialog} this
33013      */
33014     alignTo : function(element, position, offsets){
33015         this.xy = this.el.getAlignToXY(element, position, offsets);
33016         if(this.isVisible()){
33017             this.el.setXY(this.xy);
33018             this.adjustAssets();
33019         }
33020         return this;
33021     },
33022
33023     /**
33024      * Anchors an element to another element and realigns it when the window is resized.
33025      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33026      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33027      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33028      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33029      * is a number, it is used as the buffer delay (defaults to 50ms).
33030      * @return {Roo.BasicDialog} this
33031      */
33032     anchorTo : function(el, alignment, offsets, monitorScroll){
33033         var action = function(){
33034             this.alignTo(el, alignment, offsets);
33035         };
33036         Roo.EventManager.onWindowResize(action, this);
33037         var tm = typeof monitorScroll;
33038         if(tm != 'undefined'){
33039             Roo.EventManager.on(window, 'scroll', action, this,
33040                 {buffer: tm == 'number' ? monitorScroll : 50});
33041         }
33042         action.call(this);
33043         return this;
33044     },
33045
33046     /**
33047      * Returns true if the dialog is visible
33048      * @return {Boolean}
33049      */
33050     isVisible : function(){
33051         return this.el.isVisible();
33052     },
33053
33054     // private
33055     animHide : function(callback){
33056         var b = Roo.get(this.animateTarget).getBox();
33057         this.proxy.show();
33058         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33059         this.el.hide();
33060         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33061                     this.hideEl.createDelegate(this, [callback]));
33062     },
33063
33064     /**
33065      * Hides the dialog.
33066      * @param {Function} callback (optional) Function to call when the dialog is hidden
33067      * @return {Roo.BasicDialog} this
33068      */
33069     hide : function(callback){
33070         if (this.fireEvent("beforehide", this) === false){
33071             return;
33072         }
33073         if(this.shadow){
33074             this.shadow.hide();
33075         }
33076         if(this.shim) {
33077           this.shim.hide();
33078         }
33079         // sometimes animateTarget seems to get set.. causing problems...
33080         // this just double checks..
33081         if(this.animateTarget && Roo.get(this.animateTarget)) {
33082            this.animHide(callback);
33083         }else{
33084             this.el.hide();
33085             this.hideEl(callback);
33086         }
33087         return this;
33088     },
33089
33090     // private
33091     hideEl : function(callback){
33092         this.proxy.hide();
33093         if(this.modal){
33094             this.mask.hide();
33095             Roo.get(document.body).removeClass("x-body-masked");
33096         }
33097         this.fireEvent("hide", this);
33098         if(typeof callback == "function"){
33099             callback();
33100         }
33101     },
33102
33103     // private
33104     hideAction : function(){
33105         this.setLeft("-10000px");
33106         this.setTop("-10000px");
33107         this.setStyle("visibility", "hidden");
33108     },
33109
33110     // private
33111     refreshSize : function(){
33112         this.size = this.el.getSize();
33113         this.xy = this.el.getXY();
33114         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33115     },
33116
33117     // private
33118     // z-index is managed by the DialogManager and may be overwritten at any time
33119     setZIndex : function(index){
33120         if(this.modal){
33121             this.mask.setStyle("z-index", index);
33122         }
33123         if(this.shim){
33124             this.shim.setStyle("z-index", ++index);
33125         }
33126         if(this.shadow){
33127             this.shadow.setZIndex(++index);
33128         }
33129         this.el.setStyle("z-index", ++index);
33130         if(this.proxy){
33131             this.proxy.setStyle("z-index", ++index);
33132         }
33133         if(this.resizer){
33134             this.resizer.proxy.setStyle("z-index", ++index);
33135         }
33136
33137         this.lastZIndex = index;
33138     },
33139
33140     /**
33141      * Returns the element for this dialog
33142      * @return {Roo.Element} The underlying dialog Element
33143      */
33144     getEl : function(){
33145         return this.el;
33146     }
33147 });
33148
33149 /**
33150  * @class Roo.DialogManager
33151  * Provides global access to BasicDialogs that have been created and
33152  * support for z-indexing (layering) multiple open dialogs.
33153  */
33154 Roo.DialogManager = function(){
33155     var list = {};
33156     var accessList = [];
33157     var front = null;
33158
33159     // private
33160     var sortDialogs = function(d1, d2){
33161         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33162     };
33163
33164     // private
33165     var orderDialogs = function(){
33166         accessList.sort(sortDialogs);
33167         var seed = Roo.DialogManager.zseed;
33168         for(var i = 0, len = accessList.length; i < len; i++){
33169             var dlg = accessList[i];
33170             if(dlg){
33171                 dlg.setZIndex(seed + (i*10));
33172             }
33173         }
33174     };
33175
33176     return {
33177         /**
33178          * The starting z-index for BasicDialogs (defaults to 9000)
33179          * @type Number The z-index value
33180          */
33181         zseed : 9000,
33182
33183         // private
33184         register : function(dlg){
33185             list[dlg.id] = dlg;
33186             accessList.push(dlg);
33187         },
33188
33189         // private
33190         unregister : function(dlg){
33191             delete list[dlg.id];
33192             var i=0;
33193             var len=0;
33194             if(!accessList.indexOf){
33195                 for(  i = 0, len = accessList.length; i < len; i++){
33196                     if(accessList[i] == dlg){
33197                         accessList.splice(i, 1);
33198                         return;
33199                     }
33200                 }
33201             }else{
33202                  i = accessList.indexOf(dlg);
33203                 if(i != -1){
33204                     accessList.splice(i, 1);
33205                 }
33206             }
33207         },
33208
33209         /**
33210          * Gets a registered dialog by id
33211          * @param {String/Object} id The id of the dialog or a dialog
33212          * @return {Roo.BasicDialog} this
33213          */
33214         get : function(id){
33215             return typeof id == "object" ? id : list[id];
33216         },
33217
33218         /**
33219          * Brings the specified dialog to the front
33220          * @param {String/Object} dlg The id of the dialog or a dialog
33221          * @return {Roo.BasicDialog} this
33222          */
33223         bringToFront : function(dlg){
33224             dlg = this.get(dlg);
33225             if(dlg != front){
33226                 front = dlg;
33227                 dlg._lastAccess = new Date().getTime();
33228                 orderDialogs();
33229             }
33230             return dlg;
33231         },
33232
33233         /**
33234          * Sends the specified dialog to the back
33235          * @param {String/Object} dlg The id of the dialog or a dialog
33236          * @return {Roo.BasicDialog} this
33237          */
33238         sendToBack : function(dlg){
33239             dlg = this.get(dlg);
33240             dlg._lastAccess = -(new Date().getTime());
33241             orderDialogs();
33242             return dlg;
33243         },
33244
33245         /**
33246          * Hides all dialogs
33247          */
33248         hideAll : function(){
33249             for(var id in list){
33250                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33251                     list[id].hide();
33252                 }
33253             }
33254         }
33255     };
33256 }();
33257
33258 /**
33259  * @class Roo.LayoutDialog
33260  * @extends Roo.BasicDialog
33261  * @children Roo.ContentPanel
33262  * Dialog which provides adjustments for working with a layout in a Dialog.
33263  * Add your necessary layout config options to the dialog's config.<br>
33264  * Example usage (including a nested layout):
33265  * <pre><code>
33266 if(!dialog){
33267     dialog = new Roo.LayoutDialog("download-dlg", {
33268         modal: true,
33269         width:600,
33270         height:450,
33271         shadow:true,
33272         minWidth:500,
33273         minHeight:350,
33274         autoTabs:true,
33275         proxyDrag:true,
33276         // layout config merges with the dialog config
33277         center:{
33278             tabPosition: "top",
33279             alwaysShowTabs: true
33280         }
33281     });
33282     dialog.addKeyListener(27, dialog.hide, dialog);
33283     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33284     dialog.addButton("Build It!", this.getDownload, this);
33285
33286     // we can even add nested layouts
33287     var innerLayout = new Roo.BorderLayout("dl-inner", {
33288         east: {
33289             initialSize: 200,
33290             autoScroll:true,
33291             split:true
33292         },
33293         center: {
33294             autoScroll:true
33295         }
33296     });
33297     innerLayout.beginUpdate();
33298     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33299     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33300     innerLayout.endUpdate(true);
33301
33302     var layout = dialog.getLayout();
33303     layout.beginUpdate();
33304     layout.add("center", new Roo.ContentPanel("standard-panel",
33305                         {title: "Download the Source", fitToFrame:true}));
33306     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33307                {title: "Build your own roo.js"}));
33308     layout.getRegion("center").showPanel(sp);
33309     layout.endUpdate();
33310 }
33311 </code></pre>
33312     * @constructor
33313     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33314     * @param {Object} config configuration options
33315   */
33316 Roo.LayoutDialog = function(el, cfg){
33317     
33318     var config=  cfg;
33319     if (typeof(cfg) == 'undefined') {
33320         config = Roo.apply({}, el);
33321         // not sure why we use documentElement here.. - it should always be body.
33322         // IE7 borks horribly if we use documentElement.
33323         // webkit also does not like documentElement - it creates a body element...
33324         el = Roo.get( document.body || document.documentElement ).createChild();
33325         //config.autoCreate = true;
33326     }
33327     
33328     
33329     config.autoTabs = false;
33330     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33331     this.body.setStyle({overflow:"hidden", position:"relative"});
33332     this.layout = new Roo.BorderLayout(this.body.dom, config);
33333     this.layout.monitorWindowResize = false;
33334     this.el.addClass("x-dlg-auto-layout");
33335     // fix case when center region overwrites center function
33336     this.center = Roo.BasicDialog.prototype.center;
33337     this.on("show", this.layout.layout, this.layout, true);
33338     if (config.items) {
33339         var xitems = config.items;
33340         delete config.items;
33341         Roo.each(xitems, this.addxtype, this);
33342     }
33343     
33344     
33345 };
33346 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33347     
33348     
33349     /**
33350      * @cfg {Roo.LayoutRegion} east  
33351      */
33352     /**
33353      * @cfg {Roo.LayoutRegion} west
33354      */
33355     /**
33356      * @cfg {Roo.LayoutRegion} south
33357      */
33358     /**
33359      * @cfg {Roo.LayoutRegion} north
33360      */
33361     /**
33362      * @cfg {Roo.LayoutRegion} center
33363      */
33364     /**
33365      * @cfg {Roo.Button} buttons[]  Bottom buttons..
33366      */
33367     
33368     
33369     /**
33370      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33371      * @deprecated
33372      */
33373     endUpdate : function(){
33374         this.layout.endUpdate();
33375     },
33376
33377     /**
33378      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33379      *  @deprecated
33380      */
33381     beginUpdate : function(){
33382         this.layout.beginUpdate();
33383     },
33384
33385     /**
33386      * Get the BorderLayout for this dialog
33387      * @return {Roo.BorderLayout}
33388      */
33389     getLayout : function(){
33390         return this.layout;
33391     },
33392
33393     showEl : function(){
33394         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33395         if(Roo.isIE7){
33396             this.layout.layout();
33397         }
33398     },
33399
33400     // private
33401     // Use the syncHeightBeforeShow config option to control this automatically
33402     syncBodyHeight : function(){
33403         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33404         if(this.layout){this.layout.layout();}
33405     },
33406     
33407       /**
33408      * Add an xtype element (actually adds to the layout.)
33409      * @return {Object} xdata xtype object data.
33410      */
33411     
33412     addxtype : function(c) {
33413         return this.layout.addxtype(c);
33414     }
33415 });/*
33416  * Based on:
33417  * Ext JS Library 1.1.1
33418  * Copyright(c) 2006-2007, Ext JS, LLC.
33419  *
33420  * Originally Released Under LGPL - original licence link has changed is not relivant.
33421  *
33422  * Fork - LGPL
33423  * <script type="text/javascript">
33424  */
33425  
33426 /**
33427  * @class Roo.MessageBox
33428  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33429  * Example usage:
33430  *<pre><code>
33431 // Basic alert:
33432 Roo.Msg.alert('Status', 'Changes saved successfully.');
33433
33434 // Prompt for user data:
33435 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33436     if (btn == 'ok'){
33437         // process text value...
33438     }
33439 });
33440
33441 // Show a dialog using config options:
33442 Roo.Msg.show({
33443    title:'Save Changes?',
33444    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33445    buttons: Roo.Msg.YESNOCANCEL,
33446    fn: processResult,
33447    animEl: 'elId'
33448 });
33449 </code></pre>
33450  * @singleton
33451  */
33452 Roo.MessageBox = function(){
33453     var dlg, opt, mask, waitTimer;
33454     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33455     var buttons, activeTextEl, bwidth;
33456
33457     // private
33458     var handleButton = function(button){
33459         dlg.hide();
33460         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33461     };
33462
33463     // private
33464     var handleHide = function(){
33465         if(opt && opt.cls){
33466             dlg.el.removeClass(opt.cls);
33467         }
33468         if(waitTimer){
33469             Roo.TaskMgr.stop(waitTimer);
33470             waitTimer = null;
33471         }
33472     };
33473
33474     // private
33475     var updateButtons = function(b){
33476         var width = 0;
33477         if(!b){
33478             buttons["ok"].hide();
33479             buttons["cancel"].hide();
33480             buttons["yes"].hide();
33481             buttons["no"].hide();
33482             dlg.footer.dom.style.display = 'none';
33483             return width;
33484         }
33485         dlg.footer.dom.style.display = '';
33486         for(var k in buttons){
33487             if(typeof buttons[k] != "function"){
33488                 if(b[k]){
33489                     buttons[k].show();
33490                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33491                     width += buttons[k].el.getWidth()+15;
33492                 }else{
33493                     buttons[k].hide();
33494                 }
33495             }
33496         }
33497         return width;
33498     };
33499
33500     // private
33501     var handleEsc = function(d, k, e){
33502         if(opt && opt.closable !== false){
33503             dlg.hide();
33504         }
33505         if(e){
33506             e.stopEvent();
33507         }
33508     };
33509
33510     return {
33511         /**
33512          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33513          * @return {Roo.BasicDialog} The BasicDialog element
33514          */
33515         getDialog : function(){
33516            if(!dlg){
33517                 dlg = new Roo.BasicDialog("x-msg-box", {
33518                     autoCreate : true,
33519                     shadow: true,
33520                     draggable: true,
33521                     resizable:false,
33522                     constraintoviewport:false,
33523                     fixedcenter:true,
33524                     collapsible : false,
33525                     shim:true,
33526                     modal: true,
33527                     width:400, height:100,
33528                     buttonAlign:"center",
33529                     closeClick : function(){
33530                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33531                             handleButton("no");
33532                         }else{
33533                             handleButton("cancel");
33534                         }
33535                     }
33536                 });
33537                 dlg.on("hide", handleHide);
33538                 mask = dlg.mask;
33539                 dlg.addKeyListener(27, handleEsc);
33540                 buttons = {};
33541                 var bt = this.buttonText;
33542                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33543                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33544                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33545                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33546                 bodyEl = dlg.body.createChild({
33547
33548                     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>'
33549                 });
33550                 msgEl = bodyEl.dom.firstChild;
33551                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33552                 textboxEl.enableDisplayMode();
33553                 textboxEl.addKeyListener([10,13], function(){
33554                     if(dlg.isVisible() && opt && opt.buttons){
33555                         if(opt.buttons.ok){
33556                             handleButton("ok");
33557                         }else if(opt.buttons.yes){
33558                             handleButton("yes");
33559                         }
33560                     }
33561                 });
33562                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33563                 textareaEl.enableDisplayMode();
33564                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33565                 progressEl.enableDisplayMode();
33566                 var pf = progressEl.dom.firstChild;
33567                 if (pf) {
33568                     pp = Roo.get(pf.firstChild);
33569                     pp.setHeight(pf.offsetHeight);
33570                 }
33571                 
33572             }
33573             return dlg;
33574         },
33575
33576         /**
33577          * Updates the message box body text
33578          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33579          * the XHTML-compliant non-breaking space character '&amp;#160;')
33580          * @return {Roo.MessageBox} This message box
33581          */
33582         updateText : function(text){
33583             if(!dlg.isVisible() && !opt.width){
33584                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33585             }
33586             msgEl.innerHTML = text || '&#160;';
33587       
33588             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33589             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33590             var w = Math.max(
33591                     Math.min(opt.width || cw , this.maxWidth), 
33592                     Math.max(opt.minWidth || this.minWidth, bwidth)
33593             );
33594             if(opt.prompt){
33595                 activeTextEl.setWidth(w);
33596             }
33597             if(dlg.isVisible()){
33598                 dlg.fixedcenter = false;
33599             }
33600             // to big, make it scroll. = But as usual stupid IE does not support
33601             // !important..
33602             
33603             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33604                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33605                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33606             } else {
33607                 bodyEl.dom.style.height = '';
33608                 bodyEl.dom.style.overflowY = '';
33609             }
33610             if (cw > w) {
33611                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33612             } else {
33613                 bodyEl.dom.style.overflowX = '';
33614             }
33615             
33616             dlg.setContentSize(w, bodyEl.getHeight());
33617             if(dlg.isVisible()){
33618                 dlg.fixedcenter = true;
33619             }
33620             return this;
33621         },
33622
33623         /**
33624          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33625          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33626          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33627          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33628          * @return {Roo.MessageBox} This message box
33629          */
33630         updateProgress : function(value, text){
33631             if(text){
33632                 this.updateText(text);
33633             }
33634             if (pp) { // weird bug on my firefox - for some reason this is not defined
33635                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33636             }
33637             return this;
33638         },        
33639
33640         /**
33641          * Returns true if the message box is currently displayed
33642          * @return {Boolean} True if the message box is visible, else false
33643          */
33644         isVisible : function(){
33645             return dlg && dlg.isVisible();  
33646         },
33647
33648         /**
33649          * Hides the message box if it is displayed
33650          */
33651         hide : function(){
33652             if(this.isVisible()){
33653                 dlg.hide();
33654             }  
33655         },
33656
33657         /**
33658          * Displays a new message box, or reinitializes an existing message box, based on the config options
33659          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33660          * The following config object properties are supported:
33661          * <pre>
33662 Property    Type             Description
33663 ----------  ---------------  ------------------------------------------------------------------------------------
33664 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33665                                    closes (defaults to undefined)
33666 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33667                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33668 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33669                                    progress and wait dialogs will ignore this property and always hide the
33670                                    close button as they can only be closed programmatically.
33671 cls               String           A custom CSS class to apply to the message box element
33672 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33673                                    displayed (defaults to 75)
33674 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33675                                    function will be btn (the name of the button that was clicked, if applicable,
33676                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33677                                    Progress and wait dialogs will ignore this option since they do not respond to
33678                                    user actions and can only be closed programmatically, so any required function
33679                                    should be called by the same code after it closes the dialog.
33680 icon              String           A CSS class that provides a background image to be used as an icon for
33681                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33682 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33683 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33684 modal             Boolean          False to allow user interaction with the page while the message box is
33685                                    displayed (defaults to true)
33686 msg               String           A string that will replace the existing message box body text (defaults
33687                                    to the XHTML-compliant non-breaking space character '&#160;')
33688 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33689 progress          Boolean          True to display a progress bar (defaults to false)
33690 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33691 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33692 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33693 title             String           The title text
33694 value             String           The string value to set into the active textbox element if displayed
33695 wait              Boolean          True to display a progress bar (defaults to false)
33696 width             Number           The width of the dialog in pixels
33697 </pre>
33698          *
33699          * Example usage:
33700          * <pre><code>
33701 Roo.Msg.show({
33702    title: 'Address',
33703    msg: 'Please enter your address:',
33704    width: 300,
33705    buttons: Roo.MessageBox.OKCANCEL,
33706    multiline: true,
33707    fn: saveAddress,
33708    animEl: 'addAddressBtn'
33709 });
33710 </code></pre>
33711          * @param {Object} config Configuration options
33712          * @return {Roo.MessageBox} This message box
33713          */
33714         show : function(options)
33715         {
33716             
33717             // this causes nightmares if you show one dialog after another
33718             // especially on callbacks..
33719              
33720             if(this.isVisible()){
33721                 
33722                 this.hide();
33723                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33724                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33725                 Roo.log("New Dialog Message:" +  options.msg )
33726                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33727                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33728                 
33729             }
33730             var d = this.getDialog();
33731             opt = options;
33732             d.setTitle(opt.title || "&#160;");
33733             d.close.setDisplayed(opt.closable !== false);
33734             activeTextEl = textboxEl;
33735             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33736             if(opt.prompt){
33737                 if(opt.multiline){
33738                     textboxEl.hide();
33739                     textareaEl.show();
33740                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33741                         opt.multiline : this.defaultTextHeight);
33742                     activeTextEl = textareaEl;
33743                 }else{
33744                     textboxEl.show();
33745                     textareaEl.hide();
33746                 }
33747             }else{
33748                 textboxEl.hide();
33749                 textareaEl.hide();
33750             }
33751             progressEl.setDisplayed(opt.progress === true);
33752             this.updateProgress(0);
33753             activeTextEl.dom.value = opt.value || "";
33754             if(opt.prompt){
33755                 dlg.setDefaultButton(activeTextEl);
33756             }else{
33757                 var bs = opt.buttons;
33758                 var db = null;
33759                 if(bs && bs.ok){
33760                     db = buttons["ok"];
33761                 }else if(bs && bs.yes){
33762                     db = buttons["yes"];
33763                 }
33764                 dlg.setDefaultButton(db);
33765             }
33766             bwidth = updateButtons(opt.buttons);
33767             this.updateText(opt.msg);
33768             if(opt.cls){
33769                 d.el.addClass(opt.cls);
33770             }
33771             d.proxyDrag = opt.proxyDrag === true;
33772             d.modal = opt.modal !== false;
33773             d.mask = opt.modal !== false ? mask : false;
33774             if(!d.isVisible()){
33775                 // force it to the end of the z-index stack so it gets a cursor in FF
33776                 document.body.appendChild(dlg.el.dom);
33777                 d.animateTarget = null;
33778                 d.show(options.animEl);
33779             }
33780             return this;
33781         },
33782
33783         /**
33784          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33785          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33786          * and closing the message box when the process is complete.
33787          * @param {String} title The title bar text
33788          * @param {String} msg The message box body text
33789          * @return {Roo.MessageBox} This message box
33790          */
33791         progress : function(title, msg){
33792             this.show({
33793                 title : title,
33794                 msg : msg,
33795                 buttons: false,
33796                 progress:true,
33797                 closable:false,
33798                 minWidth: this.minProgressWidth,
33799                 modal : true
33800             });
33801             return this;
33802         },
33803
33804         /**
33805          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33806          * If a callback function is passed it will be called after the user clicks the button, and the
33807          * id of the button that was clicked will be passed as the only parameter to the callback
33808          * (could also be the top-right close button).
33809          * @param {String} title The title bar text
33810          * @param {String} msg The message box body text
33811          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33812          * @param {Object} scope (optional) The scope of the callback function
33813          * @return {Roo.MessageBox} This message box
33814          */
33815         alert : function(title, msg, fn, scope){
33816             this.show({
33817                 title : title,
33818                 msg : msg,
33819                 buttons: this.OK,
33820                 fn: fn,
33821                 scope : scope,
33822                 modal : true
33823             });
33824             return this;
33825         },
33826
33827         /**
33828          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33829          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33830          * You are responsible for closing the message box when the process is complete.
33831          * @param {String} msg The message box body text
33832          * @param {String} title (optional) The title bar text
33833          * @return {Roo.MessageBox} This message box
33834          */
33835         wait : function(msg, title){
33836             this.show({
33837                 title : title,
33838                 msg : msg,
33839                 buttons: false,
33840                 closable:false,
33841                 progress:true,
33842                 modal:true,
33843                 width:300,
33844                 wait:true
33845             });
33846             waitTimer = Roo.TaskMgr.start({
33847                 run: function(i){
33848                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33849                 },
33850                 interval: 1000
33851             });
33852             return this;
33853         },
33854
33855         /**
33856          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33857          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33858          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33859          * @param {String} title The title bar text
33860          * @param {String} msg The message box body text
33861          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33862          * @param {Object} scope (optional) The scope of the callback function
33863          * @return {Roo.MessageBox} This message box
33864          */
33865         confirm : function(title, msg, fn, scope){
33866             this.show({
33867                 title : title,
33868                 msg : msg,
33869                 buttons: this.YESNO,
33870                 fn: fn,
33871                 scope : scope,
33872                 modal : true
33873             });
33874             return this;
33875         },
33876
33877         /**
33878          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33879          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33880          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33881          * (could also be the top-right close button) and the text that was entered will be passed as the two
33882          * parameters to the callback.
33883          * @param {String} title The title bar text
33884          * @param {String} msg The message box body text
33885          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33886          * @param {Object} scope (optional) The scope of the callback function
33887          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33888          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33889          * @return {Roo.MessageBox} This message box
33890          */
33891         prompt : function(title, msg, fn, scope, multiline){
33892             this.show({
33893                 title : title,
33894                 msg : msg,
33895                 buttons: this.OKCANCEL,
33896                 fn: fn,
33897                 minWidth:250,
33898                 scope : scope,
33899                 prompt:true,
33900                 multiline: multiline,
33901                 modal : true
33902             });
33903             return this;
33904         },
33905
33906         /**
33907          * Button config that displays a single OK button
33908          * @type Object
33909          */
33910         OK : {ok:true},
33911         /**
33912          * Button config that displays Yes and No buttons
33913          * @type Object
33914          */
33915         YESNO : {yes:true, no:true},
33916         /**
33917          * Button config that displays OK and Cancel buttons
33918          * @type Object
33919          */
33920         OKCANCEL : {ok:true, cancel:true},
33921         /**
33922          * Button config that displays Yes, No and Cancel buttons
33923          * @type Object
33924          */
33925         YESNOCANCEL : {yes:true, no:true, cancel:true},
33926
33927         /**
33928          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33929          * @type Number
33930          */
33931         defaultTextHeight : 75,
33932         /**
33933          * The maximum width in pixels of the message box (defaults to 600)
33934          * @type Number
33935          */
33936         maxWidth : 600,
33937         /**
33938          * The minimum width in pixels of the message box (defaults to 100)
33939          * @type Number
33940          */
33941         minWidth : 100,
33942         /**
33943          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33944          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33945          * @type Number
33946          */
33947         minProgressWidth : 250,
33948         /**
33949          * An object containing the default button text strings that can be overriden for localized language support.
33950          * Supported properties are: ok, cancel, yes and no.
33951          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33952          * @type Object
33953          */
33954         buttonText : {
33955             ok : "OK",
33956             cancel : "Cancel",
33957             yes : "Yes",
33958             no : "No"
33959         }
33960     };
33961 }();
33962
33963 /**
33964  * Shorthand for {@link Roo.MessageBox}
33965  */
33966 Roo.Msg = Roo.MessageBox;/*
33967  * Based on:
33968  * Ext JS Library 1.1.1
33969  * Copyright(c) 2006-2007, Ext JS, LLC.
33970  *
33971  * Originally Released Under LGPL - original licence link has changed is not relivant.
33972  *
33973  * Fork - LGPL
33974  * <script type="text/javascript">
33975  */
33976 /**
33977  * @class Roo.QuickTips
33978  * Provides attractive and customizable tooltips for any element.
33979  * @singleton
33980  */
33981 Roo.QuickTips = function(){
33982     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33983     var ce, bd, xy, dd;
33984     var visible = false, disabled = true, inited = false;
33985     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33986     
33987     var onOver = function(e){
33988         if(disabled){
33989             return;
33990         }
33991         var t = e.getTarget();
33992         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33993             return;
33994         }
33995         if(ce && t == ce.el){
33996             clearTimeout(hideProc);
33997             return;
33998         }
33999         if(t && tagEls[t.id]){
34000             tagEls[t.id].el = t;
34001             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34002             return;
34003         }
34004         var ttp, et = Roo.fly(t);
34005         var ns = cfg.namespace;
34006         if(tm.interceptTitles && t.title){
34007             ttp = t.title;
34008             t.qtip = ttp;
34009             t.removeAttribute("title");
34010             e.preventDefault();
34011         }else{
34012             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34013         }
34014         if(ttp){
34015             showProc = show.defer(tm.showDelay, tm, [{
34016                 el: t, 
34017                 text: ttp.replace(/\\n/g,'<br/>'),
34018                 width: et.getAttributeNS(ns, cfg.width),
34019                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34020                 title: et.getAttributeNS(ns, cfg.title),
34021                     cls: et.getAttributeNS(ns, cfg.cls)
34022             }]);
34023         }
34024     };
34025     
34026     var onOut = function(e){
34027         clearTimeout(showProc);
34028         var t = e.getTarget();
34029         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34030             hideProc = setTimeout(hide, tm.hideDelay);
34031         }
34032     };
34033     
34034     var onMove = function(e){
34035         if(disabled){
34036             return;
34037         }
34038         xy = e.getXY();
34039         xy[1] += 18;
34040         if(tm.trackMouse && ce){
34041             el.setXY(xy);
34042         }
34043     };
34044     
34045     var onDown = function(e){
34046         clearTimeout(showProc);
34047         clearTimeout(hideProc);
34048         if(!e.within(el)){
34049             if(tm.hideOnClick){
34050                 hide();
34051                 tm.disable();
34052                 tm.enable.defer(100, tm);
34053             }
34054         }
34055     };
34056     
34057     var getPad = function(){
34058         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34059     };
34060
34061     var show = function(o){
34062         if(disabled){
34063             return;
34064         }
34065         clearTimeout(dismissProc);
34066         ce = o;
34067         if(removeCls){ // in case manually hidden
34068             el.removeClass(removeCls);
34069             removeCls = null;
34070         }
34071         if(ce.cls){
34072             el.addClass(ce.cls);
34073             removeCls = ce.cls;
34074         }
34075         if(ce.title){
34076             tipTitle.update(ce.title);
34077             tipTitle.show();
34078         }else{
34079             tipTitle.update('');
34080             tipTitle.hide();
34081         }
34082         el.dom.style.width  = tm.maxWidth+'px';
34083         //tipBody.dom.style.width = '';
34084         tipBodyText.update(o.text);
34085         var p = getPad(), w = ce.width;
34086         if(!w){
34087             var td = tipBodyText.dom;
34088             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34089             if(aw > tm.maxWidth){
34090                 w = tm.maxWidth;
34091             }else if(aw < tm.minWidth){
34092                 w = tm.minWidth;
34093             }else{
34094                 w = aw;
34095             }
34096         }
34097         //tipBody.setWidth(w);
34098         el.setWidth(parseInt(w, 10) + p);
34099         if(ce.autoHide === false){
34100             close.setDisplayed(true);
34101             if(dd){
34102                 dd.unlock();
34103             }
34104         }else{
34105             close.setDisplayed(false);
34106             if(dd){
34107                 dd.lock();
34108             }
34109         }
34110         if(xy){
34111             el.avoidY = xy[1]-18;
34112             el.setXY(xy);
34113         }
34114         if(tm.animate){
34115             el.setOpacity(.1);
34116             el.setStyle("visibility", "visible");
34117             el.fadeIn({callback: afterShow});
34118         }else{
34119             afterShow();
34120         }
34121     };
34122     
34123     var afterShow = function(){
34124         if(ce){
34125             el.show();
34126             esc.enable();
34127             if(tm.autoDismiss && ce.autoHide !== false){
34128                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34129             }
34130         }
34131     };
34132     
34133     var hide = function(noanim){
34134         clearTimeout(dismissProc);
34135         clearTimeout(hideProc);
34136         ce = null;
34137         if(el.isVisible()){
34138             esc.disable();
34139             if(noanim !== true && tm.animate){
34140                 el.fadeOut({callback: afterHide});
34141             }else{
34142                 afterHide();
34143             } 
34144         }
34145     };
34146     
34147     var afterHide = function(){
34148         el.hide();
34149         if(removeCls){
34150             el.removeClass(removeCls);
34151             removeCls = null;
34152         }
34153     };
34154     
34155     return {
34156         /**
34157         * @cfg {Number} minWidth
34158         * The minimum width of the quick tip (defaults to 40)
34159         */
34160        minWidth : 40,
34161         /**
34162         * @cfg {Number} maxWidth
34163         * The maximum width of the quick tip (defaults to 300)
34164         */
34165        maxWidth : 300,
34166         /**
34167         * @cfg {Boolean} interceptTitles
34168         * True to automatically use the element's DOM title value if available (defaults to false)
34169         */
34170        interceptTitles : false,
34171         /**
34172         * @cfg {Boolean} trackMouse
34173         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34174         */
34175        trackMouse : false,
34176         /**
34177         * @cfg {Boolean} hideOnClick
34178         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34179         */
34180        hideOnClick : true,
34181         /**
34182         * @cfg {Number} showDelay
34183         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34184         */
34185        showDelay : 500,
34186         /**
34187         * @cfg {Number} hideDelay
34188         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34189         */
34190        hideDelay : 200,
34191         /**
34192         * @cfg {Boolean} autoHide
34193         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34194         * Used in conjunction with hideDelay.
34195         */
34196        autoHide : true,
34197         /**
34198         * @cfg {Boolean}
34199         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34200         * (defaults to true).  Used in conjunction with autoDismissDelay.
34201         */
34202        autoDismiss : true,
34203         /**
34204         * @cfg {Number}
34205         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34206         */
34207        autoDismissDelay : 5000,
34208        /**
34209         * @cfg {Boolean} animate
34210         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34211         */
34212        animate : false,
34213
34214        /**
34215         * @cfg {String} title
34216         * Title text to display (defaults to '').  This can be any valid HTML markup.
34217         */
34218         title: '',
34219        /**
34220         * @cfg {String} text
34221         * Body text to display (defaults to '').  This can be any valid HTML markup.
34222         */
34223         text : '',
34224        /**
34225         * @cfg {String} cls
34226         * A CSS class to apply to the base quick tip element (defaults to '').
34227         */
34228         cls : '',
34229        /**
34230         * @cfg {Number} width
34231         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34232         * minWidth or maxWidth.
34233         */
34234         width : null,
34235
34236     /**
34237      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34238      * or display QuickTips in a page.
34239      */
34240        init : function(){
34241           tm = Roo.QuickTips;
34242           cfg = tm.tagConfig;
34243           if(!inited){
34244               if(!Roo.isReady){ // allow calling of init() before onReady
34245                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34246                   return;
34247               }
34248               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34249               el.fxDefaults = {stopFx: true};
34250               // maximum custom styling
34251               //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>');
34252               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>');              
34253               tipTitle = el.child('h3');
34254               tipTitle.enableDisplayMode("block");
34255               tipBody = el.child('div.x-tip-bd');
34256               tipBodyText = el.child('div.x-tip-bd-inner');
34257               //bdLeft = el.child('div.x-tip-bd-left');
34258               //bdRight = el.child('div.x-tip-bd-right');
34259               close = el.child('div.x-tip-close');
34260               close.enableDisplayMode("block");
34261               close.on("click", hide);
34262               var d = Roo.get(document);
34263               d.on("mousedown", onDown);
34264               d.on("mouseover", onOver);
34265               d.on("mouseout", onOut);
34266               d.on("mousemove", onMove);
34267               esc = d.addKeyListener(27, hide);
34268               esc.disable();
34269               if(Roo.dd.DD){
34270                   dd = el.initDD("default", null, {
34271                       onDrag : function(){
34272                           el.sync();  
34273                       }
34274                   });
34275                   dd.setHandleElId(tipTitle.id);
34276                   dd.lock();
34277               }
34278               inited = true;
34279           }
34280           this.enable(); 
34281        },
34282
34283     /**
34284      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34285      * are supported:
34286      * <pre>
34287 Property    Type                   Description
34288 ----------  ---------------------  ------------------------------------------------------------------------
34289 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34290      * </ul>
34291      * @param {Object} config The config object
34292      */
34293        register : function(config){
34294            var cs = config instanceof Array ? config : arguments;
34295            for(var i = 0, len = cs.length; i < len; i++) {
34296                var c = cs[i];
34297                var target = c.target;
34298                if(target){
34299                    if(target instanceof Array){
34300                        for(var j = 0, jlen = target.length; j < jlen; j++){
34301                            tagEls[target[j]] = c;
34302                        }
34303                    }else{
34304                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34305                    }
34306                }
34307            }
34308        },
34309
34310     /**
34311      * Removes this quick tip from its element and destroys it.
34312      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34313      */
34314        unregister : function(el){
34315            delete tagEls[Roo.id(el)];
34316        },
34317
34318     /**
34319      * Enable this quick tip.
34320      */
34321        enable : function(){
34322            if(inited && disabled){
34323                locks.pop();
34324                if(locks.length < 1){
34325                    disabled = false;
34326                }
34327            }
34328        },
34329
34330     /**
34331      * Disable this quick tip.
34332      */
34333        disable : function(){
34334           disabled = true;
34335           clearTimeout(showProc);
34336           clearTimeout(hideProc);
34337           clearTimeout(dismissProc);
34338           if(ce){
34339               hide(true);
34340           }
34341           locks.push(1);
34342        },
34343
34344     /**
34345      * Returns true if the quick tip is enabled, else false.
34346      */
34347        isEnabled : function(){
34348             return !disabled;
34349        },
34350
34351         // private
34352        tagConfig : {
34353            namespace : "roo", // was ext?? this may break..
34354            alt_namespace : "ext",
34355            attribute : "qtip",
34356            width : "width",
34357            target : "target",
34358            title : "qtitle",
34359            hide : "hide",
34360            cls : "qclass"
34361        }
34362    };
34363 }();
34364
34365 // backwards compat
34366 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34367  * Based on:
34368  * Ext JS Library 1.1.1
34369  * Copyright(c) 2006-2007, Ext JS, LLC.
34370  *
34371  * Originally Released Under LGPL - original licence link has changed is not relivant.
34372  *
34373  * Fork - LGPL
34374  * <script type="text/javascript">
34375  */
34376  
34377
34378 /**
34379  * @class Roo.tree.TreePanel
34380  * @extends Roo.data.Tree
34381  * @cfg {Roo.tree.TreeNode} root The root node
34382  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34383  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34384  * @cfg {Boolean} enableDD true to enable drag and drop
34385  * @cfg {Boolean} enableDrag true to enable just drag
34386  * @cfg {Boolean} enableDrop true to enable just drop
34387  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34388  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34389  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34390  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34391  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34392  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34393  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34394  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34395  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34396  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34397  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34398  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
34399  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
34400  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34401  * @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>
34402  * @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>
34403  * 
34404  * @constructor
34405  * @param {String/HTMLElement/Element} el The container element
34406  * @param {Object} config
34407  */
34408 Roo.tree.TreePanel = function(el, config){
34409     var root = false;
34410     var loader = false;
34411     if (config.root) {
34412         root = config.root;
34413         delete config.root;
34414     }
34415     if (config.loader) {
34416         loader = config.loader;
34417         delete config.loader;
34418     }
34419     
34420     Roo.apply(this, config);
34421     Roo.tree.TreePanel.superclass.constructor.call(this);
34422     this.el = Roo.get(el);
34423     this.el.addClass('x-tree');
34424     //console.log(root);
34425     if (root) {
34426         this.setRootNode( Roo.factory(root, Roo.tree));
34427     }
34428     if (loader) {
34429         this.loader = Roo.factory(loader, Roo.tree);
34430     }
34431    /**
34432     * Read-only. The id of the container element becomes this TreePanel's id.
34433     */
34434     this.id = this.el.id;
34435     this.addEvents({
34436         /**
34437         * @event beforeload
34438         * Fires before a node is loaded, return false to cancel
34439         * @param {Node} node The node being loaded
34440         */
34441         "beforeload" : true,
34442         /**
34443         * @event load
34444         * Fires when a node is loaded
34445         * @param {Node} node The node that was loaded
34446         */
34447         "load" : true,
34448         /**
34449         * @event textchange
34450         * Fires when the text for a node is changed
34451         * @param {Node} node The node
34452         * @param {String} text The new text
34453         * @param {String} oldText The old text
34454         */
34455         "textchange" : true,
34456         /**
34457         * @event beforeexpand
34458         * Fires before a node is expanded, return false to cancel.
34459         * @param {Node} node The node
34460         * @param {Boolean} deep
34461         * @param {Boolean} anim
34462         */
34463         "beforeexpand" : true,
34464         /**
34465         * @event beforecollapse
34466         * Fires before a node is collapsed, return false to cancel.
34467         * @param {Node} node The node
34468         * @param {Boolean} deep
34469         * @param {Boolean} anim
34470         */
34471         "beforecollapse" : true,
34472         /**
34473         * @event expand
34474         * Fires when a node is expanded
34475         * @param {Node} node The node
34476         */
34477         "expand" : true,
34478         /**
34479         * @event disabledchange
34480         * Fires when the disabled status of a node changes
34481         * @param {Node} node The node
34482         * @param {Boolean} disabled
34483         */
34484         "disabledchange" : true,
34485         /**
34486         * @event collapse
34487         * Fires when a node is collapsed
34488         * @param {Node} node The node
34489         */
34490         "collapse" : true,
34491         /**
34492         * @event beforeclick
34493         * Fires before click processing on a node. Return false to cancel the default action.
34494         * @param {Node} node The node
34495         * @param {Roo.EventObject} e The event object
34496         */
34497         "beforeclick":true,
34498         /**
34499         * @event checkchange
34500         * Fires when a node with a checkbox's checked property changes
34501         * @param {Node} this This node
34502         * @param {Boolean} checked
34503         */
34504         "checkchange":true,
34505         /**
34506         * @event click
34507         * Fires when a node is clicked
34508         * @param {Node} node The node
34509         * @param {Roo.EventObject} e The event object
34510         */
34511         "click":true,
34512         /**
34513         * @event dblclick
34514         * Fires when a node is double clicked
34515         * @param {Node} node The node
34516         * @param {Roo.EventObject} e The event object
34517         */
34518         "dblclick":true,
34519         /**
34520         * @event contextmenu
34521         * Fires when a node is right clicked
34522         * @param {Node} node The node
34523         * @param {Roo.EventObject} e The event object
34524         */
34525         "contextmenu":true,
34526         /**
34527         * @event beforechildrenrendered
34528         * Fires right before the child nodes for a node are rendered
34529         * @param {Node} node The node
34530         */
34531         "beforechildrenrendered":true,
34532         /**
34533         * @event startdrag
34534         * Fires when a node starts being dragged
34535         * @param {Roo.tree.TreePanel} this
34536         * @param {Roo.tree.TreeNode} node
34537         * @param {event} e The raw browser event
34538         */ 
34539        "startdrag" : true,
34540        /**
34541         * @event enddrag
34542         * Fires when a drag operation is complete
34543         * @param {Roo.tree.TreePanel} this
34544         * @param {Roo.tree.TreeNode} node
34545         * @param {event} e The raw browser event
34546         */
34547        "enddrag" : true,
34548        /**
34549         * @event dragdrop
34550         * Fires when a dragged node is dropped on a valid DD target
34551         * @param {Roo.tree.TreePanel} this
34552         * @param {Roo.tree.TreeNode} node
34553         * @param {DD} dd The dd it was dropped on
34554         * @param {event} e The raw browser event
34555         */
34556        "dragdrop" : true,
34557        /**
34558         * @event beforenodedrop
34559         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34560         * passed to handlers has the following properties:<br />
34561         * <ul style="padding:5px;padding-left:16px;">
34562         * <li>tree - The TreePanel</li>
34563         * <li>target - The node being targeted for the drop</li>
34564         * <li>data - The drag data from the drag source</li>
34565         * <li>point - The point of the drop - append, above or below</li>
34566         * <li>source - The drag source</li>
34567         * <li>rawEvent - Raw mouse event</li>
34568         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34569         * to be inserted by setting them on this object.</li>
34570         * <li>cancel - Set this to true to cancel the drop.</li>
34571         * </ul>
34572         * @param {Object} dropEvent
34573         */
34574        "beforenodedrop" : true,
34575        /**
34576         * @event nodedrop
34577         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34578         * passed to handlers has the following properties:<br />
34579         * <ul style="padding:5px;padding-left:16px;">
34580         * <li>tree - The TreePanel</li>
34581         * <li>target - The node being targeted for the drop</li>
34582         * <li>data - The drag data from the drag source</li>
34583         * <li>point - The point of the drop - append, above or below</li>
34584         * <li>source - The drag source</li>
34585         * <li>rawEvent - Raw mouse event</li>
34586         * <li>dropNode - Dropped node(s).</li>
34587         * </ul>
34588         * @param {Object} dropEvent
34589         */
34590        "nodedrop" : true,
34591         /**
34592         * @event nodedragover
34593         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34594         * passed to handlers has the following properties:<br />
34595         * <ul style="padding:5px;padding-left:16px;">
34596         * <li>tree - The TreePanel</li>
34597         * <li>target - The node being targeted for the drop</li>
34598         * <li>data - The drag data from the drag source</li>
34599         * <li>point - The point of the drop - append, above or below</li>
34600         * <li>source - The drag source</li>
34601         * <li>rawEvent - Raw mouse event</li>
34602         * <li>dropNode - Drop node(s) provided by the source.</li>
34603         * <li>cancel - Set this to true to signal drop not allowed.</li>
34604         * </ul>
34605         * @param {Object} dragOverEvent
34606         */
34607        "nodedragover" : true,
34608        /**
34609         * @event appendnode
34610         * Fires when append node to the tree
34611         * @param {Roo.tree.TreePanel} this
34612         * @param {Roo.tree.TreeNode} node
34613         * @param {Number} index The index of the newly appended node
34614         */
34615        "appendnode" : true
34616         
34617     });
34618     if(this.singleExpand){
34619        this.on("beforeexpand", this.restrictExpand, this);
34620     }
34621     if (this.editor) {
34622         this.editor.tree = this;
34623         this.editor = Roo.factory(this.editor, Roo.tree);
34624     }
34625     
34626     if (this.selModel) {
34627         this.selModel = Roo.factory(this.selModel, Roo.tree);
34628     }
34629    
34630 };
34631 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34632     rootVisible : true,
34633     animate: Roo.enableFx,
34634     lines : true,
34635     enableDD : false,
34636     hlDrop : Roo.enableFx,
34637   
34638     renderer: false,
34639     
34640     rendererTip: false,
34641     // private
34642     restrictExpand : function(node){
34643         var p = node.parentNode;
34644         if(p){
34645             if(p.expandedChild && p.expandedChild.parentNode == p){
34646                 p.expandedChild.collapse();
34647             }
34648             p.expandedChild = node;
34649         }
34650     },
34651
34652     // private override
34653     setRootNode : function(node){
34654         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34655         if(!this.rootVisible){
34656             node.ui = new Roo.tree.RootTreeNodeUI(node);
34657         }
34658         return node;
34659     },
34660
34661     /**
34662      * Returns the container element for this TreePanel
34663      */
34664     getEl : function(){
34665         return this.el;
34666     },
34667
34668     /**
34669      * Returns the default TreeLoader for this TreePanel
34670      */
34671     getLoader : function(){
34672         return this.loader;
34673     },
34674
34675     /**
34676      * Expand all nodes
34677      */
34678     expandAll : function(){
34679         this.root.expand(true);
34680     },
34681
34682     /**
34683      * Collapse all nodes
34684      */
34685     collapseAll : function(){
34686         this.root.collapse(true);
34687     },
34688
34689     /**
34690      * Returns the selection model used by this TreePanel
34691      */
34692     getSelectionModel : function(){
34693         if(!this.selModel){
34694             this.selModel = new Roo.tree.DefaultSelectionModel();
34695         }
34696         return this.selModel;
34697     },
34698
34699     /**
34700      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34701      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34702      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34703      * @return {Array}
34704      */
34705     getChecked : function(a, startNode){
34706         startNode = startNode || this.root;
34707         var r = [];
34708         var f = function(){
34709             if(this.attributes.checked){
34710                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34711             }
34712         }
34713         startNode.cascade(f);
34714         return r;
34715     },
34716
34717     /**
34718      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34719      * @param {String} path
34720      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34721      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34722      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34723      */
34724     expandPath : function(path, attr, callback){
34725         attr = attr || "id";
34726         var keys = path.split(this.pathSeparator);
34727         var curNode = this.root;
34728         if(curNode.attributes[attr] != keys[1]){ // invalid root
34729             if(callback){
34730                 callback(false, null);
34731             }
34732             return;
34733         }
34734         var index = 1;
34735         var f = function(){
34736             if(++index == keys.length){
34737                 if(callback){
34738                     callback(true, curNode);
34739                 }
34740                 return;
34741             }
34742             var c = curNode.findChild(attr, keys[index]);
34743             if(!c){
34744                 if(callback){
34745                     callback(false, curNode);
34746                 }
34747                 return;
34748             }
34749             curNode = c;
34750             c.expand(false, false, f);
34751         };
34752         curNode.expand(false, false, f);
34753     },
34754
34755     /**
34756      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34757      * @param {String} path
34758      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34759      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34760      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34761      */
34762     selectPath : function(path, attr, callback){
34763         attr = attr || "id";
34764         var keys = path.split(this.pathSeparator);
34765         var v = keys.pop();
34766         if(keys.length > 0){
34767             var f = function(success, node){
34768                 if(success && node){
34769                     var n = node.findChild(attr, v);
34770                     if(n){
34771                         n.select();
34772                         if(callback){
34773                             callback(true, n);
34774                         }
34775                     }else if(callback){
34776                         callback(false, n);
34777                     }
34778                 }else{
34779                     if(callback){
34780                         callback(false, n);
34781                     }
34782                 }
34783             };
34784             this.expandPath(keys.join(this.pathSeparator), attr, f);
34785         }else{
34786             this.root.select();
34787             if(callback){
34788                 callback(true, this.root);
34789             }
34790         }
34791     },
34792
34793     getTreeEl : function(){
34794         return this.el;
34795     },
34796
34797     /**
34798      * Trigger rendering of this TreePanel
34799      */
34800     render : function(){
34801         if (this.innerCt) {
34802             return this; // stop it rendering more than once!!
34803         }
34804         
34805         this.innerCt = this.el.createChild({tag:"ul",
34806                cls:"x-tree-root-ct " +
34807                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34808
34809         if(this.containerScroll){
34810             Roo.dd.ScrollManager.register(this.el);
34811         }
34812         if((this.enableDD || this.enableDrop) && !this.dropZone){
34813            /**
34814             * The dropZone used by this tree if drop is enabled
34815             * @type Roo.tree.TreeDropZone
34816             */
34817              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34818                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34819            });
34820         }
34821         if((this.enableDD || this.enableDrag) && !this.dragZone){
34822            /**
34823             * The dragZone used by this tree if drag is enabled
34824             * @type Roo.tree.TreeDragZone
34825             */
34826             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34827                ddGroup: this.ddGroup || "TreeDD",
34828                scroll: this.ddScroll
34829            });
34830         }
34831         this.getSelectionModel().init(this);
34832         if (!this.root) {
34833             Roo.log("ROOT not set in tree");
34834             return this;
34835         }
34836         this.root.render();
34837         if(!this.rootVisible){
34838             this.root.renderChildren();
34839         }
34840         return this;
34841     }
34842 });/*
34843  * Based on:
34844  * Ext JS Library 1.1.1
34845  * Copyright(c) 2006-2007, Ext JS, LLC.
34846  *
34847  * Originally Released Under LGPL - original licence link has changed is not relivant.
34848  *
34849  * Fork - LGPL
34850  * <script type="text/javascript">
34851  */
34852  
34853
34854 /**
34855  * @class Roo.tree.DefaultSelectionModel
34856  * @extends Roo.util.Observable
34857  * The default single selection for a TreePanel.
34858  * @param {Object} cfg Configuration
34859  */
34860 Roo.tree.DefaultSelectionModel = function(cfg){
34861    this.selNode = null;
34862    
34863    
34864    
34865    this.addEvents({
34866        /**
34867         * @event selectionchange
34868         * Fires when the selected node changes
34869         * @param {DefaultSelectionModel} this
34870         * @param {TreeNode} node the new selection
34871         */
34872        "selectionchange" : true,
34873
34874        /**
34875         * @event beforeselect
34876         * Fires before the selected node changes, return false to cancel the change
34877         * @param {DefaultSelectionModel} this
34878         * @param {TreeNode} node the new selection
34879         * @param {TreeNode} node the old selection
34880         */
34881        "beforeselect" : true
34882    });
34883    
34884     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34885 };
34886
34887 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34888     init : function(tree){
34889         this.tree = tree;
34890         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34891         tree.on("click", this.onNodeClick, this);
34892     },
34893     
34894     onNodeClick : function(node, e){
34895         if (e.ctrlKey && this.selNode == node)  {
34896             this.unselect(node);
34897             return;
34898         }
34899         this.select(node);
34900     },
34901     
34902     /**
34903      * Select a node.
34904      * @param {TreeNode} node The node to select
34905      * @return {TreeNode} The selected node
34906      */
34907     select : function(node){
34908         var last = this.selNode;
34909         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34910             if(last){
34911                 last.ui.onSelectedChange(false);
34912             }
34913             this.selNode = node;
34914             node.ui.onSelectedChange(true);
34915             this.fireEvent("selectionchange", this, node, last);
34916         }
34917         return node;
34918     },
34919     
34920     /**
34921      * Deselect a node.
34922      * @param {TreeNode} node The node to unselect
34923      */
34924     unselect : function(node){
34925         if(this.selNode == node){
34926             this.clearSelections();
34927         }    
34928     },
34929     
34930     /**
34931      * Clear all selections
34932      */
34933     clearSelections : function(){
34934         var n = this.selNode;
34935         if(n){
34936             n.ui.onSelectedChange(false);
34937             this.selNode = null;
34938             this.fireEvent("selectionchange", this, null);
34939         }
34940         return n;
34941     },
34942     
34943     /**
34944      * Get the selected node
34945      * @return {TreeNode} The selected node
34946      */
34947     getSelectedNode : function(){
34948         return this.selNode;    
34949     },
34950     
34951     /**
34952      * Returns true if the node is selected
34953      * @param {TreeNode} node The node to check
34954      * @return {Boolean}
34955      */
34956     isSelected : function(node){
34957         return this.selNode == node;  
34958     },
34959
34960     /**
34961      * Selects the node above the selected node in the tree, intelligently walking the nodes
34962      * @return TreeNode The new selection
34963      */
34964     selectPrevious : function(){
34965         var s = this.selNode || this.lastSelNode;
34966         if(!s){
34967             return null;
34968         }
34969         var ps = s.previousSibling;
34970         if(ps){
34971             if(!ps.isExpanded() || ps.childNodes.length < 1){
34972                 return this.select(ps);
34973             } else{
34974                 var lc = ps.lastChild;
34975                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34976                     lc = lc.lastChild;
34977                 }
34978                 return this.select(lc);
34979             }
34980         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34981             return this.select(s.parentNode);
34982         }
34983         return null;
34984     },
34985
34986     /**
34987      * Selects the node above the selected node in the tree, intelligently walking the nodes
34988      * @return TreeNode The new selection
34989      */
34990     selectNext : function(){
34991         var s = this.selNode || this.lastSelNode;
34992         if(!s){
34993             return null;
34994         }
34995         if(s.firstChild && s.isExpanded()){
34996              return this.select(s.firstChild);
34997          }else if(s.nextSibling){
34998              return this.select(s.nextSibling);
34999          }else if(s.parentNode){
35000             var newS = null;
35001             s.parentNode.bubble(function(){
35002                 if(this.nextSibling){
35003                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35004                     return false;
35005                 }
35006             });
35007             return newS;
35008          }
35009         return null;
35010     },
35011
35012     onKeyDown : function(e){
35013         var s = this.selNode || this.lastSelNode;
35014         // undesirable, but required
35015         var sm = this;
35016         if(!s){
35017             return;
35018         }
35019         var k = e.getKey();
35020         switch(k){
35021              case e.DOWN:
35022                  e.stopEvent();
35023                  this.selectNext();
35024              break;
35025              case e.UP:
35026                  e.stopEvent();
35027                  this.selectPrevious();
35028              break;
35029              case e.RIGHT:
35030                  e.preventDefault();
35031                  if(s.hasChildNodes()){
35032                      if(!s.isExpanded()){
35033                          s.expand();
35034                      }else if(s.firstChild){
35035                          this.select(s.firstChild, e);
35036                      }
35037                  }
35038              break;
35039              case e.LEFT:
35040                  e.preventDefault();
35041                  if(s.hasChildNodes() && s.isExpanded()){
35042                      s.collapse();
35043                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35044                      this.select(s.parentNode, e);
35045                  }
35046              break;
35047         };
35048     }
35049 });
35050
35051 /**
35052  * @class Roo.tree.MultiSelectionModel
35053  * @extends Roo.util.Observable
35054  * Multi selection for a TreePanel.
35055  * @param {Object} cfg Configuration
35056  */
35057 Roo.tree.MultiSelectionModel = function(){
35058    this.selNodes = [];
35059    this.selMap = {};
35060    this.addEvents({
35061        /**
35062         * @event selectionchange
35063         * Fires when the selected nodes change
35064         * @param {MultiSelectionModel} this
35065         * @param {Array} nodes Array of the selected nodes
35066         */
35067        "selectionchange" : true
35068    });
35069    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35070    
35071 };
35072
35073 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35074     init : function(tree){
35075         this.tree = tree;
35076         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35077         tree.on("click", this.onNodeClick, this);
35078     },
35079     
35080     onNodeClick : function(node, e){
35081         this.select(node, e, e.ctrlKey);
35082     },
35083     
35084     /**
35085      * Select a node.
35086      * @param {TreeNode} node The node to select
35087      * @param {EventObject} e (optional) An event associated with the selection
35088      * @param {Boolean} keepExisting True to retain existing selections
35089      * @return {TreeNode} The selected node
35090      */
35091     select : function(node, e, keepExisting){
35092         if(keepExisting !== true){
35093             this.clearSelections(true);
35094         }
35095         if(this.isSelected(node)){
35096             this.lastSelNode = node;
35097             return node;
35098         }
35099         this.selNodes.push(node);
35100         this.selMap[node.id] = node;
35101         this.lastSelNode = node;
35102         node.ui.onSelectedChange(true);
35103         this.fireEvent("selectionchange", this, this.selNodes);
35104         return node;
35105     },
35106     
35107     /**
35108      * Deselect a node.
35109      * @param {TreeNode} node The node to unselect
35110      */
35111     unselect : function(node){
35112         if(this.selMap[node.id]){
35113             node.ui.onSelectedChange(false);
35114             var sn = this.selNodes;
35115             var index = -1;
35116             if(sn.indexOf){
35117                 index = sn.indexOf(node);
35118             }else{
35119                 for(var i = 0, len = sn.length; i < len; i++){
35120                     if(sn[i] == node){
35121                         index = i;
35122                         break;
35123                     }
35124                 }
35125             }
35126             if(index != -1){
35127                 this.selNodes.splice(index, 1);
35128             }
35129             delete this.selMap[node.id];
35130             this.fireEvent("selectionchange", this, this.selNodes);
35131         }
35132     },
35133     
35134     /**
35135      * Clear all selections
35136      */
35137     clearSelections : function(suppressEvent){
35138         var sn = this.selNodes;
35139         if(sn.length > 0){
35140             for(var i = 0, len = sn.length; i < len; i++){
35141                 sn[i].ui.onSelectedChange(false);
35142             }
35143             this.selNodes = [];
35144             this.selMap = {};
35145             if(suppressEvent !== true){
35146                 this.fireEvent("selectionchange", this, this.selNodes);
35147             }
35148         }
35149     },
35150     
35151     /**
35152      * Returns true if the node is selected
35153      * @param {TreeNode} node The node to check
35154      * @return {Boolean}
35155      */
35156     isSelected : function(node){
35157         return this.selMap[node.id] ? true : false;  
35158     },
35159     
35160     /**
35161      * Returns an array of the selected nodes
35162      * @return {Array}
35163      */
35164     getSelectedNodes : function(){
35165         return this.selNodes;    
35166     },
35167
35168     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35169
35170     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35171
35172     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35173 });/*
35174  * Based on:
35175  * Ext JS Library 1.1.1
35176  * Copyright(c) 2006-2007, Ext JS, LLC.
35177  *
35178  * Originally Released Under LGPL - original licence link has changed is not relivant.
35179  *
35180  * Fork - LGPL
35181  * <script type="text/javascript">
35182  */
35183  
35184 /**
35185  * @class Roo.tree.TreeNode
35186  * @extends Roo.data.Node
35187  * @cfg {String} text The text for this node
35188  * @cfg {Boolean} expanded true to start the node expanded
35189  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35190  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35191  * @cfg {Boolean} disabled true to start the node disabled
35192  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35193  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35194  * @cfg {String} cls A css class to be added to the node
35195  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35196  * @cfg {String} href URL of the link used for the node (defaults to #)
35197  * @cfg {String} hrefTarget target frame for the link
35198  * @cfg {String} qtip An Ext QuickTip for the node
35199  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35200  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35201  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35202  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35203  * (defaults to undefined with no checkbox rendered)
35204  * @constructor
35205  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35206  */
35207 Roo.tree.TreeNode = function(attributes){
35208     attributes = attributes || {};
35209     if(typeof attributes == "string"){
35210         attributes = {text: attributes};
35211     }
35212     this.childrenRendered = false;
35213     this.rendered = false;
35214     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35215     this.expanded = attributes.expanded === true;
35216     this.isTarget = attributes.isTarget !== false;
35217     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35218     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35219
35220     /**
35221      * Read-only. The text for this node. To change it use setText().
35222      * @type String
35223      */
35224     this.text = attributes.text;
35225     /**
35226      * True if this node is disabled.
35227      * @type Boolean
35228      */
35229     this.disabled = attributes.disabled === true;
35230
35231     this.addEvents({
35232         /**
35233         * @event textchange
35234         * Fires when the text for this node is changed
35235         * @param {Node} this This node
35236         * @param {String} text The new text
35237         * @param {String} oldText The old text
35238         */
35239         "textchange" : true,
35240         /**
35241         * @event beforeexpand
35242         * Fires before this node is expanded, return false to cancel.
35243         * @param {Node} this This node
35244         * @param {Boolean} deep
35245         * @param {Boolean} anim
35246         */
35247         "beforeexpand" : true,
35248         /**
35249         * @event beforecollapse
35250         * Fires before this node is collapsed, return false to cancel.
35251         * @param {Node} this This node
35252         * @param {Boolean} deep
35253         * @param {Boolean} anim
35254         */
35255         "beforecollapse" : true,
35256         /**
35257         * @event expand
35258         * Fires when this node is expanded
35259         * @param {Node} this This node
35260         */
35261         "expand" : true,
35262         /**
35263         * @event disabledchange
35264         * Fires when the disabled status of this node changes
35265         * @param {Node} this This node
35266         * @param {Boolean} disabled
35267         */
35268         "disabledchange" : true,
35269         /**
35270         * @event collapse
35271         * Fires when this node is collapsed
35272         * @param {Node} this This node
35273         */
35274         "collapse" : true,
35275         /**
35276         * @event beforeclick
35277         * Fires before click processing. Return false to cancel the default action.
35278         * @param {Node} this This node
35279         * @param {Roo.EventObject} e The event object
35280         */
35281         "beforeclick":true,
35282         /**
35283         * @event checkchange
35284         * Fires when a node with a checkbox's checked property changes
35285         * @param {Node} this This node
35286         * @param {Boolean} checked
35287         */
35288         "checkchange":true,
35289         /**
35290         * @event click
35291         * Fires when this node is clicked
35292         * @param {Node} this This node
35293         * @param {Roo.EventObject} e The event object
35294         */
35295         "click":true,
35296         /**
35297         * @event dblclick
35298         * Fires when this node is double clicked
35299         * @param {Node} this This node
35300         * @param {Roo.EventObject} e The event object
35301         */
35302         "dblclick":true,
35303         /**
35304         * @event contextmenu
35305         * Fires when this node is right clicked
35306         * @param {Node} this This node
35307         * @param {Roo.EventObject} e The event object
35308         */
35309         "contextmenu":true,
35310         /**
35311         * @event beforechildrenrendered
35312         * Fires right before the child nodes for this node are rendered
35313         * @param {Node} this This node
35314         */
35315         "beforechildrenrendered":true
35316     });
35317
35318     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35319
35320     /**
35321      * Read-only. The UI for this node
35322      * @type TreeNodeUI
35323      */
35324     this.ui = new uiClass(this);
35325     
35326     // finally support items[]
35327     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35328         return;
35329     }
35330     
35331     
35332     Roo.each(this.attributes.items, function(c) {
35333         this.appendChild(Roo.factory(c,Roo.Tree));
35334     }, this);
35335     delete this.attributes.items;
35336     
35337     
35338     
35339 };
35340 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35341     preventHScroll: true,
35342     /**
35343      * Returns true if this node is expanded
35344      * @return {Boolean}
35345      */
35346     isExpanded : function(){
35347         return this.expanded;
35348     },
35349
35350     /**
35351      * Returns the UI object for this node
35352      * @return {TreeNodeUI}
35353      */
35354     getUI : function(){
35355         return this.ui;
35356     },
35357
35358     // private override
35359     setFirstChild : function(node){
35360         var of = this.firstChild;
35361         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35362         if(this.childrenRendered && of && node != of){
35363             of.renderIndent(true, true);
35364         }
35365         if(this.rendered){
35366             this.renderIndent(true, true);
35367         }
35368     },
35369
35370     // private override
35371     setLastChild : function(node){
35372         var ol = this.lastChild;
35373         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35374         if(this.childrenRendered && ol && node != ol){
35375             ol.renderIndent(true, true);
35376         }
35377         if(this.rendered){
35378             this.renderIndent(true, true);
35379         }
35380     },
35381
35382     // these methods are overridden to provide lazy rendering support
35383     // private override
35384     appendChild : function()
35385     {
35386         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35387         if(node && this.childrenRendered){
35388             node.render();
35389         }
35390         this.ui.updateExpandIcon();
35391         return node;
35392     },
35393
35394     // private override
35395     removeChild : function(node){
35396         this.ownerTree.getSelectionModel().unselect(node);
35397         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35398         // if it's been rendered remove dom node
35399         if(this.childrenRendered){
35400             node.ui.remove();
35401         }
35402         if(this.childNodes.length < 1){
35403             this.collapse(false, false);
35404         }else{
35405             this.ui.updateExpandIcon();
35406         }
35407         if(!this.firstChild) {
35408             this.childrenRendered = false;
35409         }
35410         return node;
35411     },
35412
35413     // private override
35414     insertBefore : function(node, refNode){
35415         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35416         if(newNode && refNode && this.childrenRendered){
35417             node.render();
35418         }
35419         this.ui.updateExpandIcon();
35420         return newNode;
35421     },
35422
35423     /**
35424      * Sets the text for this node
35425      * @param {String} text
35426      */
35427     setText : function(text){
35428         var oldText = this.text;
35429         this.text = text;
35430         this.attributes.text = text;
35431         if(this.rendered){ // event without subscribing
35432             this.ui.onTextChange(this, text, oldText);
35433         }
35434         this.fireEvent("textchange", this, text, oldText);
35435     },
35436
35437     /**
35438      * Triggers selection of this node
35439      */
35440     select : function(){
35441         this.getOwnerTree().getSelectionModel().select(this);
35442     },
35443
35444     /**
35445      * Triggers deselection of this node
35446      */
35447     unselect : function(){
35448         this.getOwnerTree().getSelectionModel().unselect(this);
35449     },
35450
35451     /**
35452      * Returns true if this node is selected
35453      * @return {Boolean}
35454      */
35455     isSelected : function(){
35456         return this.getOwnerTree().getSelectionModel().isSelected(this);
35457     },
35458
35459     /**
35460      * Expand this node.
35461      * @param {Boolean} deep (optional) True to expand all children as well
35462      * @param {Boolean} anim (optional) false to cancel the default animation
35463      * @param {Function} callback (optional) A callback to be called when
35464      * expanding this node completes (does not wait for deep expand to complete).
35465      * Called with 1 parameter, this node.
35466      */
35467     expand : function(deep, anim, callback){
35468         if(!this.expanded){
35469             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35470                 return;
35471             }
35472             if(!this.childrenRendered){
35473                 this.renderChildren();
35474             }
35475             this.expanded = true;
35476             
35477             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35478                 this.ui.animExpand(function(){
35479                     this.fireEvent("expand", this);
35480                     if(typeof callback == "function"){
35481                         callback(this);
35482                     }
35483                     if(deep === true){
35484                         this.expandChildNodes(true);
35485                     }
35486                 }.createDelegate(this));
35487                 return;
35488             }else{
35489                 this.ui.expand();
35490                 this.fireEvent("expand", this);
35491                 if(typeof callback == "function"){
35492                     callback(this);
35493                 }
35494             }
35495         }else{
35496            if(typeof callback == "function"){
35497                callback(this);
35498            }
35499         }
35500         if(deep === true){
35501             this.expandChildNodes(true);
35502         }
35503     },
35504
35505     isHiddenRoot : function(){
35506         return this.isRoot && !this.getOwnerTree().rootVisible;
35507     },
35508
35509     /**
35510      * Collapse this node.
35511      * @param {Boolean} deep (optional) True to collapse all children as well
35512      * @param {Boolean} anim (optional) false to cancel the default animation
35513      */
35514     collapse : function(deep, anim){
35515         if(this.expanded && !this.isHiddenRoot()){
35516             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35517                 return;
35518             }
35519             this.expanded = false;
35520             if((this.getOwnerTree().animate && anim !== false) || anim){
35521                 this.ui.animCollapse(function(){
35522                     this.fireEvent("collapse", this);
35523                     if(deep === true){
35524                         this.collapseChildNodes(true);
35525                     }
35526                 }.createDelegate(this));
35527                 return;
35528             }else{
35529                 this.ui.collapse();
35530                 this.fireEvent("collapse", this);
35531             }
35532         }
35533         if(deep === true){
35534             var cs = this.childNodes;
35535             for(var i = 0, len = cs.length; i < len; i++) {
35536                 cs[i].collapse(true, false);
35537             }
35538         }
35539     },
35540
35541     // private
35542     delayedExpand : function(delay){
35543         if(!this.expandProcId){
35544             this.expandProcId = this.expand.defer(delay, this);
35545         }
35546     },
35547
35548     // private
35549     cancelExpand : function(){
35550         if(this.expandProcId){
35551             clearTimeout(this.expandProcId);
35552         }
35553         this.expandProcId = false;
35554     },
35555
35556     /**
35557      * Toggles expanded/collapsed state of the node
35558      */
35559     toggle : function(){
35560         if(this.expanded){
35561             this.collapse();
35562         }else{
35563             this.expand();
35564         }
35565     },
35566
35567     /**
35568      * Ensures all parent nodes are expanded
35569      */
35570     ensureVisible : function(callback){
35571         var tree = this.getOwnerTree();
35572         tree.expandPath(this.parentNode.getPath(), false, function(){
35573             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35574             Roo.callback(callback);
35575         }.createDelegate(this));
35576     },
35577
35578     /**
35579      * Expand all child nodes
35580      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35581      */
35582     expandChildNodes : function(deep){
35583         var cs = this.childNodes;
35584         for(var i = 0, len = cs.length; i < len; i++) {
35585                 cs[i].expand(deep);
35586         }
35587     },
35588
35589     /**
35590      * Collapse all child nodes
35591      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35592      */
35593     collapseChildNodes : function(deep){
35594         var cs = this.childNodes;
35595         for(var i = 0, len = cs.length; i < len; i++) {
35596                 cs[i].collapse(deep);
35597         }
35598     },
35599
35600     /**
35601      * Disables this node
35602      */
35603     disable : function(){
35604         this.disabled = true;
35605         this.unselect();
35606         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35607             this.ui.onDisableChange(this, true);
35608         }
35609         this.fireEvent("disabledchange", this, true);
35610     },
35611
35612     /**
35613      * Enables this node
35614      */
35615     enable : function(){
35616         this.disabled = false;
35617         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35618             this.ui.onDisableChange(this, false);
35619         }
35620         this.fireEvent("disabledchange", this, false);
35621     },
35622
35623     // private
35624     renderChildren : function(suppressEvent){
35625         if(suppressEvent !== false){
35626             this.fireEvent("beforechildrenrendered", this);
35627         }
35628         var cs = this.childNodes;
35629         for(var i = 0, len = cs.length; i < len; i++){
35630             cs[i].render(true);
35631         }
35632         this.childrenRendered = true;
35633     },
35634
35635     // private
35636     sort : function(fn, scope){
35637         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35638         if(this.childrenRendered){
35639             var cs = this.childNodes;
35640             for(var i = 0, len = cs.length; i < len; i++){
35641                 cs[i].render(true);
35642             }
35643         }
35644     },
35645
35646     // private
35647     render : function(bulkRender){
35648         this.ui.render(bulkRender);
35649         if(!this.rendered){
35650             this.rendered = true;
35651             if(this.expanded){
35652                 this.expanded = false;
35653                 this.expand(false, false);
35654             }
35655         }
35656     },
35657
35658     // private
35659     renderIndent : function(deep, refresh){
35660         if(refresh){
35661             this.ui.childIndent = null;
35662         }
35663         this.ui.renderIndent();
35664         if(deep === true && this.childrenRendered){
35665             var cs = this.childNodes;
35666             for(var i = 0, len = cs.length; i < len; i++){
35667                 cs[i].renderIndent(true, refresh);
35668             }
35669         }
35670     }
35671 });/*
35672  * Based on:
35673  * Ext JS Library 1.1.1
35674  * Copyright(c) 2006-2007, Ext JS, LLC.
35675  *
35676  * Originally Released Under LGPL - original licence link has changed is not relivant.
35677  *
35678  * Fork - LGPL
35679  * <script type="text/javascript">
35680  */
35681  
35682 /**
35683  * @class Roo.tree.AsyncTreeNode
35684  * @extends Roo.tree.TreeNode
35685  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35686  * @constructor
35687  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35688  */
35689  Roo.tree.AsyncTreeNode = function(config){
35690     this.loaded = false;
35691     this.loading = false;
35692     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35693     /**
35694     * @event beforeload
35695     * Fires before this node is loaded, return false to cancel
35696     * @param {Node} this This node
35697     */
35698     this.addEvents({'beforeload':true, 'load': true});
35699     /**
35700     * @event load
35701     * Fires when this node is loaded
35702     * @param {Node} this This node
35703     */
35704     /**
35705      * The loader used by this node (defaults to using the tree's defined loader)
35706      * @type TreeLoader
35707      * @property loader
35708      */
35709 };
35710 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35711     expand : function(deep, anim, callback){
35712         if(this.loading){ // if an async load is already running, waiting til it's done
35713             var timer;
35714             var f = function(){
35715                 if(!this.loading){ // done loading
35716                     clearInterval(timer);
35717                     this.expand(deep, anim, callback);
35718                 }
35719             }.createDelegate(this);
35720             timer = setInterval(f, 200);
35721             return;
35722         }
35723         if(!this.loaded){
35724             if(this.fireEvent("beforeload", this) === false){
35725                 return;
35726             }
35727             this.loading = true;
35728             this.ui.beforeLoad(this);
35729             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35730             if(loader){
35731                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35732                 return;
35733             }
35734         }
35735         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35736     },
35737     
35738     /**
35739      * Returns true if this node is currently loading
35740      * @return {Boolean}
35741      */
35742     isLoading : function(){
35743         return this.loading;  
35744     },
35745     
35746     loadComplete : function(deep, anim, callback){
35747         this.loading = false;
35748         this.loaded = true;
35749         this.ui.afterLoad(this);
35750         this.fireEvent("load", this);
35751         this.expand(deep, anim, callback);
35752     },
35753     
35754     /**
35755      * Returns true if this node has been loaded
35756      * @return {Boolean}
35757      */
35758     isLoaded : function(){
35759         return this.loaded;
35760     },
35761     
35762     hasChildNodes : function(){
35763         if(!this.isLeaf() && !this.loaded){
35764             return true;
35765         }else{
35766             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35767         }
35768     },
35769
35770     /**
35771      * Trigger a reload for this node
35772      * @param {Function} callback
35773      */
35774     reload : function(callback){
35775         this.collapse(false, false);
35776         while(this.firstChild){
35777             this.removeChild(this.firstChild);
35778         }
35779         this.childrenRendered = false;
35780         this.loaded = false;
35781         if(this.isHiddenRoot()){
35782             this.expanded = false;
35783         }
35784         this.expand(false, false, callback);
35785     }
35786 });/*
35787  * Based on:
35788  * Ext JS Library 1.1.1
35789  * Copyright(c) 2006-2007, Ext JS, LLC.
35790  *
35791  * Originally Released Under LGPL - original licence link has changed is not relivant.
35792  *
35793  * Fork - LGPL
35794  * <script type="text/javascript">
35795  */
35796  
35797 /**
35798  * @class Roo.tree.TreeNodeUI
35799  * @constructor
35800  * @param {Object} node The node to render
35801  * The TreeNode UI implementation is separate from the
35802  * tree implementation. Unless you are customizing the tree UI,
35803  * you should never have to use this directly.
35804  */
35805 Roo.tree.TreeNodeUI = function(node){
35806     this.node = node;
35807     this.rendered = false;
35808     this.animating = false;
35809     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35810 };
35811
35812 Roo.tree.TreeNodeUI.prototype = {
35813     removeChild : function(node){
35814         if(this.rendered){
35815             this.ctNode.removeChild(node.ui.getEl());
35816         }
35817     },
35818
35819     beforeLoad : function(){
35820          this.addClass("x-tree-node-loading");
35821     },
35822
35823     afterLoad : function(){
35824          this.removeClass("x-tree-node-loading");
35825     },
35826
35827     onTextChange : function(node, text, oldText){
35828         if(this.rendered){
35829             this.textNode.innerHTML = text;
35830         }
35831     },
35832
35833     onDisableChange : function(node, state){
35834         this.disabled = state;
35835         if(state){
35836             this.addClass("x-tree-node-disabled");
35837         }else{
35838             this.removeClass("x-tree-node-disabled");
35839         }
35840     },
35841
35842     onSelectedChange : function(state){
35843         if(state){
35844             this.focus();
35845             this.addClass("x-tree-selected");
35846         }else{
35847             //this.blur();
35848             this.removeClass("x-tree-selected");
35849         }
35850     },
35851
35852     onMove : function(tree, node, oldParent, newParent, index, refNode){
35853         this.childIndent = null;
35854         if(this.rendered){
35855             var targetNode = newParent.ui.getContainer();
35856             if(!targetNode){//target not rendered
35857                 this.holder = document.createElement("div");
35858                 this.holder.appendChild(this.wrap);
35859                 return;
35860             }
35861             var insertBefore = refNode ? refNode.ui.getEl() : null;
35862             if(insertBefore){
35863                 targetNode.insertBefore(this.wrap, insertBefore);
35864             }else{
35865                 targetNode.appendChild(this.wrap);
35866             }
35867             this.node.renderIndent(true);
35868         }
35869     },
35870
35871     addClass : function(cls){
35872         if(this.elNode){
35873             Roo.fly(this.elNode).addClass(cls);
35874         }
35875     },
35876
35877     removeClass : function(cls){
35878         if(this.elNode){
35879             Roo.fly(this.elNode).removeClass(cls);
35880         }
35881     },
35882
35883     remove : function(){
35884         if(this.rendered){
35885             this.holder = document.createElement("div");
35886             this.holder.appendChild(this.wrap);
35887         }
35888     },
35889
35890     fireEvent : function(){
35891         return this.node.fireEvent.apply(this.node, arguments);
35892     },
35893
35894     initEvents : function(){
35895         this.node.on("move", this.onMove, this);
35896         var E = Roo.EventManager;
35897         var a = this.anchor;
35898
35899         var el = Roo.fly(a, '_treeui');
35900
35901         if(Roo.isOpera){ // opera render bug ignores the CSS
35902             el.setStyle("text-decoration", "none");
35903         }
35904
35905         el.on("click", this.onClick, this);
35906         el.on("dblclick", this.onDblClick, this);
35907
35908         if(this.checkbox){
35909             Roo.EventManager.on(this.checkbox,
35910                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35911         }
35912
35913         el.on("contextmenu", this.onContextMenu, this);
35914
35915         var icon = Roo.fly(this.iconNode);
35916         icon.on("click", this.onClick, this);
35917         icon.on("dblclick", this.onDblClick, this);
35918         icon.on("contextmenu", this.onContextMenu, this);
35919         E.on(this.ecNode, "click", this.ecClick, this, true);
35920
35921         if(this.node.disabled){
35922             this.addClass("x-tree-node-disabled");
35923         }
35924         if(this.node.hidden){
35925             this.addClass("x-tree-node-disabled");
35926         }
35927         var ot = this.node.getOwnerTree();
35928         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35929         if(dd && (!this.node.isRoot || ot.rootVisible)){
35930             Roo.dd.Registry.register(this.elNode, {
35931                 node: this.node,
35932                 handles: this.getDDHandles(),
35933                 isHandle: false
35934             });
35935         }
35936     },
35937
35938     getDDHandles : function(){
35939         return [this.iconNode, this.textNode];
35940     },
35941
35942     hide : function(){
35943         if(this.rendered){
35944             this.wrap.style.display = "none";
35945         }
35946     },
35947
35948     show : function(){
35949         if(this.rendered){
35950             this.wrap.style.display = "";
35951         }
35952     },
35953
35954     onContextMenu : function(e){
35955         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35956             e.preventDefault();
35957             this.focus();
35958             this.fireEvent("contextmenu", this.node, e);
35959         }
35960     },
35961
35962     onClick : function(e){
35963         if(this.dropping){
35964             e.stopEvent();
35965             return;
35966         }
35967         if(this.fireEvent("beforeclick", this.node, e) !== false){
35968             if(!this.disabled && this.node.attributes.href){
35969                 this.fireEvent("click", this.node, e);
35970                 return;
35971             }
35972             e.preventDefault();
35973             if(this.disabled){
35974                 return;
35975             }
35976
35977             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35978                 this.node.toggle();
35979             }
35980
35981             this.fireEvent("click", this.node, e);
35982         }else{
35983             e.stopEvent();
35984         }
35985     },
35986
35987     onDblClick : function(e){
35988         e.preventDefault();
35989         if(this.disabled){
35990             return;
35991         }
35992         if(this.checkbox){
35993             this.toggleCheck();
35994         }
35995         if(!this.animating && this.node.hasChildNodes()){
35996             this.node.toggle();
35997         }
35998         this.fireEvent("dblclick", this.node, e);
35999     },
36000
36001     onCheckChange : function(){
36002         var checked = this.checkbox.checked;
36003         this.node.attributes.checked = checked;
36004         this.fireEvent('checkchange', this.node, checked);
36005     },
36006
36007     ecClick : function(e){
36008         if(!this.animating && this.node.hasChildNodes()){
36009             this.node.toggle();
36010         }
36011     },
36012
36013     startDrop : function(){
36014         this.dropping = true;
36015     },
36016
36017     // delayed drop so the click event doesn't get fired on a drop
36018     endDrop : function(){
36019        setTimeout(function(){
36020            this.dropping = false;
36021        }.createDelegate(this), 50);
36022     },
36023
36024     expand : function(){
36025         this.updateExpandIcon();
36026         this.ctNode.style.display = "";
36027     },
36028
36029     focus : function(){
36030         if(!this.node.preventHScroll){
36031             try{this.anchor.focus();
36032             }catch(e){}
36033         }else if(!Roo.isIE){
36034             try{
36035                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36036                 var l = noscroll.scrollLeft;
36037                 this.anchor.focus();
36038                 noscroll.scrollLeft = l;
36039             }catch(e){}
36040         }
36041     },
36042
36043     toggleCheck : function(value){
36044         var cb = this.checkbox;
36045         if(cb){
36046             cb.checked = (value === undefined ? !cb.checked : value);
36047         }
36048     },
36049
36050     blur : function(){
36051         try{
36052             this.anchor.blur();
36053         }catch(e){}
36054     },
36055
36056     animExpand : function(callback){
36057         var ct = Roo.get(this.ctNode);
36058         ct.stopFx();
36059         if(!this.node.hasChildNodes()){
36060             this.updateExpandIcon();
36061             this.ctNode.style.display = "";
36062             Roo.callback(callback);
36063             return;
36064         }
36065         this.animating = true;
36066         this.updateExpandIcon();
36067
36068         ct.slideIn('t', {
36069            callback : function(){
36070                this.animating = false;
36071                Roo.callback(callback);
36072             },
36073             scope: this,
36074             duration: this.node.ownerTree.duration || .25
36075         });
36076     },
36077
36078     highlight : function(){
36079         var tree = this.node.getOwnerTree();
36080         Roo.fly(this.wrap).highlight(
36081             tree.hlColor || "C3DAF9",
36082             {endColor: tree.hlBaseColor}
36083         );
36084     },
36085
36086     collapse : function(){
36087         this.updateExpandIcon();
36088         this.ctNode.style.display = "none";
36089     },
36090
36091     animCollapse : function(callback){
36092         var ct = Roo.get(this.ctNode);
36093         ct.enableDisplayMode('block');
36094         ct.stopFx();
36095
36096         this.animating = true;
36097         this.updateExpandIcon();
36098
36099         ct.slideOut('t', {
36100             callback : function(){
36101                this.animating = false;
36102                Roo.callback(callback);
36103             },
36104             scope: this,
36105             duration: this.node.ownerTree.duration || .25
36106         });
36107     },
36108
36109     getContainer : function(){
36110         return this.ctNode;
36111     },
36112
36113     getEl : function(){
36114         return this.wrap;
36115     },
36116
36117     appendDDGhost : function(ghostNode){
36118         ghostNode.appendChild(this.elNode.cloneNode(true));
36119     },
36120
36121     getDDRepairXY : function(){
36122         return Roo.lib.Dom.getXY(this.iconNode);
36123     },
36124
36125     onRender : function(){
36126         this.render();
36127     },
36128
36129     render : function(bulkRender){
36130         var n = this.node, a = n.attributes;
36131         var targetNode = n.parentNode ?
36132               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36133
36134         if(!this.rendered){
36135             this.rendered = true;
36136
36137             this.renderElements(n, a, targetNode, bulkRender);
36138
36139             if(a.qtip){
36140                if(this.textNode.setAttributeNS){
36141                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36142                    if(a.qtipTitle){
36143                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36144                    }
36145                }else{
36146                    this.textNode.setAttribute("ext:qtip", a.qtip);
36147                    if(a.qtipTitle){
36148                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36149                    }
36150                }
36151             }else if(a.qtipCfg){
36152                 a.qtipCfg.target = Roo.id(this.textNode);
36153                 Roo.QuickTips.register(a.qtipCfg);
36154             }
36155             this.initEvents();
36156             if(!this.node.expanded){
36157                 this.updateExpandIcon();
36158             }
36159         }else{
36160             if(bulkRender === true) {
36161                 targetNode.appendChild(this.wrap);
36162             }
36163         }
36164     },
36165
36166     renderElements : function(n, a, targetNode, bulkRender)
36167     {
36168         // add some indent caching, this helps performance when rendering a large tree
36169         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36170         var t = n.getOwnerTree();
36171         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36172         if (typeof(n.attributes.html) != 'undefined') {
36173             txt = n.attributes.html;
36174         }
36175         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36176         var cb = typeof a.checked == 'boolean';
36177         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36178         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36179             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36180             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36181             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36182             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36183             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36184              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36185                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36186             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36187             "</li>"];
36188
36189         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36190             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36191                                 n.nextSibling.ui.getEl(), buf.join(""));
36192         }else{
36193             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36194         }
36195
36196         this.elNode = this.wrap.childNodes[0];
36197         this.ctNode = this.wrap.childNodes[1];
36198         var cs = this.elNode.childNodes;
36199         this.indentNode = cs[0];
36200         this.ecNode = cs[1];
36201         this.iconNode = cs[2];
36202         var index = 3;
36203         if(cb){
36204             this.checkbox = cs[3];
36205             index++;
36206         }
36207         this.anchor = cs[index];
36208         this.textNode = cs[index].firstChild;
36209     },
36210
36211     getAnchor : function(){
36212         return this.anchor;
36213     },
36214
36215     getTextEl : function(){
36216         return this.textNode;
36217     },
36218
36219     getIconEl : function(){
36220         return this.iconNode;
36221     },
36222
36223     isChecked : function(){
36224         return this.checkbox ? this.checkbox.checked : false;
36225     },
36226
36227     updateExpandIcon : function(){
36228         if(this.rendered){
36229             var n = this.node, c1, c2;
36230             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36231             var hasChild = n.hasChildNodes();
36232             if(hasChild){
36233                 if(n.expanded){
36234                     cls += "-minus";
36235                     c1 = "x-tree-node-collapsed";
36236                     c2 = "x-tree-node-expanded";
36237                 }else{
36238                     cls += "-plus";
36239                     c1 = "x-tree-node-expanded";
36240                     c2 = "x-tree-node-collapsed";
36241                 }
36242                 if(this.wasLeaf){
36243                     this.removeClass("x-tree-node-leaf");
36244                     this.wasLeaf = false;
36245                 }
36246                 if(this.c1 != c1 || this.c2 != c2){
36247                     Roo.fly(this.elNode).replaceClass(c1, c2);
36248                     this.c1 = c1; this.c2 = c2;
36249                 }
36250             }else{
36251                 // this changes non-leafs into leafs if they have no children.
36252                 // it's not very rational behaviour..
36253                 
36254                 if(!this.wasLeaf && this.node.leaf){
36255                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36256                     delete this.c1;
36257                     delete this.c2;
36258                     this.wasLeaf = true;
36259                 }
36260             }
36261             var ecc = "x-tree-ec-icon "+cls;
36262             if(this.ecc != ecc){
36263                 this.ecNode.className = ecc;
36264                 this.ecc = ecc;
36265             }
36266         }
36267     },
36268
36269     getChildIndent : function(){
36270         if(!this.childIndent){
36271             var buf = [];
36272             var p = this.node;
36273             while(p){
36274                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36275                     if(!p.isLast()) {
36276                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36277                     } else {
36278                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36279                     }
36280                 }
36281                 p = p.parentNode;
36282             }
36283             this.childIndent = buf.join("");
36284         }
36285         return this.childIndent;
36286     },
36287
36288     renderIndent : function(){
36289         if(this.rendered){
36290             var indent = "";
36291             var p = this.node.parentNode;
36292             if(p){
36293                 indent = p.ui.getChildIndent();
36294             }
36295             if(this.indentMarkup != indent){ // don't rerender if not required
36296                 this.indentNode.innerHTML = indent;
36297                 this.indentMarkup = indent;
36298             }
36299             this.updateExpandIcon();
36300         }
36301     }
36302 };
36303
36304 Roo.tree.RootTreeNodeUI = function(){
36305     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36306 };
36307 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36308     render : function(){
36309         if(!this.rendered){
36310             var targetNode = this.node.ownerTree.innerCt.dom;
36311             this.node.expanded = true;
36312             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36313             this.wrap = this.ctNode = targetNode.firstChild;
36314         }
36315     },
36316     collapse : function(){
36317     },
36318     expand : function(){
36319     }
36320 });/*
36321  * Based on:
36322  * Ext JS Library 1.1.1
36323  * Copyright(c) 2006-2007, Ext JS, LLC.
36324  *
36325  * Originally Released Under LGPL - original licence link has changed is not relivant.
36326  *
36327  * Fork - LGPL
36328  * <script type="text/javascript">
36329  */
36330 /**
36331  * @class Roo.tree.TreeLoader
36332  * @extends Roo.util.Observable
36333  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36334  * nodes from a specified URL. The response must be a javascript Array definition
36335  * who's elements are node definition objects. eg:
36336  * <pre><code>
36337 {  success : true,
36338    data :      [
36339    
36340     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36341     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36342     ]
36343 }
36344
36345
36346 </code></pre>
36347  * <br><br>
36348  * The old style respose with just an array is still supported, but not recommended.
36349  * <br><br>
36350  *
36351  * A server request is sent, and child nodes are loaded only when a node is expanded.
36352  * The loading node's id is passed to the server under the parameter name "node" to
36353  * enable the server to produce the correct child nodes.
36354  * <br><br>
36355  * To pass extra parameters, an event handler may be attached to the "beforeload"
36356  * event, and the parameters specified in the TreeLoader's baseParams property:
36357  * <pre><code>
36358     myTreeLoader.on("beforeload", function(treeLoader, node) {
36359         this.baseParams.category = node.attributes.category;
36360     }, this);
36361     
36362 </code></pre>
36363  *
36364  * This would pass an HTTP parameter called "category" to the server containing
36365  * the value of the Node's "category" attribute.
36366  * @constructor
36367  * Creates a new Treeloader.
36368  * @param {Object} config A config object containing config properties.
36369  */
36370 Roo.tree.TreeLoader = function(config){
36371     this.baseParams = {};
36372     this.requestMethod = "POST";
36373     Roo.apply(this, config);
36374
36375     this.addEvents({
36376     
36377         /**
36378          * @event beforeload
36379          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36380          * @param {Object} This TreeLoader object.
36381          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36382          * @param {Object} callback The callback function specified in the {@link #load} call.
36383          */
36384         beforeload : true,
36385         /**
36386          * @event load
36387          * Fires when the node has been successfuly loaded.
36388          * @param {Object} This TreeLoader object.
36389          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36390          * @param {Object} response The response object containing the data from the server.
36391          */
36392         load : true,
36393         /**
36394          * @event loadexception
36395          * Fires if the network request failed.
36396          * @param {Object} This TreeLoader object.
36397          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36398          * @param {Object} response The response object containing the data from the server.
36399          */
36400         loadexception : true,
36401         /**
36402          * @event create
36403          * Fires before a node is created, enabling you to return custom Node types 
36404          * @param {Object} This TreeLoader object.
36405          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36406          */
36407         create : true
36408     });
36409
36410     Roo.tree.TreeLoader.superclass.constructor.call(this);
36411 };
36412
36413 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36414     /**
36415     * @cfg {String} dataUrl The URL from which to request a Json string which
36416     * specifies an array of node definition object representing the child nodes
36417     * to be loaded.
36418     */
36419     /**
36420     * @cfg {String} requestMethod either GET or POST
36421     * defaults to POST (due to BC)
36422     * to be loaded.
36423     */
36424     /**
36425     * @cfg {Object} baseParams (optional) An object containing properties which
36426     * specify HTTP parameters to be passed to each request for child nodes.
36427     */
36428     /**
36429     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36430     * created by this loader. If the attributes sent by the server have an attribute in this object,
36431     * they take priority.
36432     */
36433     /**
36434     * @cfg {Object} uiProviders (optional) An object containing properties which
36435     * 
36436     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36437     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36438     * <i>uiProvider</i> attribute of a returned child node is a string rather
36439     * than a reference to a TreeNodeUI implementation, this that string value
36440     * is used as a property name in the uiProviders object. You can define the provider named
36441     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36442     */
36443     uiProviders : {},
36444
36445     /**
36446     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36447     * child nodes before loading.
36448     */
36449     clearOnLoad : true,
36450
36451     /**
36452     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36453     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36454     * Grid query { data : [ .....] }
36455     */
36456     
36457     root : false,
36458      /**
36459     * @cfg {String} queryParam (optional) 
36460     * Name of the query as it will be passed on the querystring (defaults to 'node')
36461     * eg. the request will be ?node=[id]
36462     */
36463     
36464     
36465     queryParam: false,
36466     
36467     /**
36468      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36469      * This is called automatically when a node is expanded, but may be used to reload
36470      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36471      * @param {Roo.tree.TreeNode} node
36472      * @param {Function} callback
36473      */
36474     load : function(node, callback){
36475         if(this.clearOnLoad){
36476             while(node.firstChild){
36477                 node.removeChild(node.firstChild);
36478             }
36479         }
36480         if(node.attributes.children){ // preloaded json children
36481             var cs = node.attributes.children;
36482             for(var i = 0, len = cs.length; i < len; i++){
36483                 node.appendChild(this.createNode(cs[i]));
36484             }
36485             if(typeof callback == "function"){
36486                 callback();
36487             }
36488         }else if(this.dataUrl){
36489             this.requestData(node, callback);
36490         }
36491     },
36492
36493     getParams: function(node){
36494         var buf = [], bp = this.baseParams;
36495         for(var key in bp){
36496             if(typeof bp[key] != "function"){
36497                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36498             }
36499         }
36500         var n = this.queryParam === false ? 'node' : this.queryParam;
36501         buf.push(n + "=", encodeURIComponent(node.id));
36502         return buf.join("");
36503     },
36504
36505     requestData : function(node, callback){
36506         if(this.fireEvent("beforeload", this, node, callback) !== false){
36507             this.transId = Roo.Ajax.request({
36508                 method:this.requestMethod,
36509                 url: this.dataUrl||this.url,
36510                 success: this.handleResponse,
36511                 failure: this.handleFailure,
36512                 scope: this,
36513                 argument: {callback: callback, node: node},
36514                 params: this.getParams(node)
36515             });
36516         }else{
36517             // if the load is cancelled, make sure we notify
36518             // the node that we are done
36519             if(typeof callback == "function"){
36520                 callback();
36521             }
36522         }
36523     },
36524
36525     isLoading : function(){
36526         return this.transId ? true : false;
36527     },
36528
36529     abort : function(){
36530         if(this.isLoading()){
36531             Roo.Ajax.abort(this.transId);
36532         }
36533     },
36534
36535     // private
36536     createNode : function(attr)
36537     {
36538         // apply baseAttrs, nice idea Corey!
36539         if(this.baseAttrs){
36540             Roo.applyIf(attr, this.baseAttrs);
36541         }
36542         if(this.applyLoader !== false){
36543             attr.loader = this;
36544         }
36545         // uiProvider = depreciated..
36546         
36547         if(typeof(attr.uiProvider) == 'string'){
36548            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36549                 /**  eval:var:attr */ eval(attr.uiProvider);
36550         }
36551         if(typeof(this.uiProviders['default']) != 'undefined') {
36552             attr.uiProvider = this.uiProviders['default'];
36553         }
36554         
36555         this.fireEvent('create', this, attr);
36556         
36557         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36558         return(attr.leaf ?
36559                         new Roo.tree.TreeNode(attr) :
36560                         new Roo.tree.AsyncTreeNode(attr));
36561     },
36562
36563     processResponse : function(response, node, callback)
36564     {
36565         var json = response.responseText;
36566         try {
36567             
36568             var o = Roo.decode(json);
36569             
36570             if (this.root === false && typeof(o.success) != undefined) {
36571                 this.root = 'data'; // the default behaviour for list like data..
36572                 }
36573                 
36574             if (this.root !== false &&  !o.success) {
36575                 // it's a failure condition.
36576                 var a = response.argument;
36577                 this.fireEvent("loadexception", this, a.node, response);
36578                 Roo.log("Load failed - should have a handler really");
36579                 return;
36580             }
36581             
36582             
36583             
36584             if (this.root !== false) {
36585                  o = o[this.root];
36586             }
36587             
36588             for(var i = 0, len = o.length; i < len; i++){
36589                 var n = this.createNode(o[i]);
36590                 if(n){
36591                     node.appendChild(n);
36592                 }
36593             }
36594             if(typeof callback == "function"){
36595                 callback(this, node);
36596             }
36597         }catch(e){
36598             this.handleFailure(response);
36599         }
36600     },
36601
36602     handleResponse : function(response){
36603         this.transId = false;
36604         var a = response.argument;
36605         this.processResponse(response, a.node, a.callback);
36606         this.fireEvent("load", this, a.node, response);
36607     },
36608
36609     handleFailure : function(response)
36610     {
36611         // should handle failure better..
36612         this.transId = false;
36613         var a = response.argument;
36614         this.fireEvent("loadexception", this, a.node, response);
36615         if(typeof a.callback == "function"){
36616             a.callback(this, a.node);
36617         }
36618     }
36619 });/*
36620  * Based on:
36621  * Ext JS Library 1.1.1
36622  * Copyright(c) 2006-2007, Ext JS, LLC.
36623  *
36624  * Originally Released Under LGPL - original licence link has changed is not relivant.
36625  *
36626  * Fork - LGPL
36627  * <script type="text/javascript">
36628  */
36629
36630 /**
36631 * @class Roo.tree.TreeFilter
36632 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36633 * @param {TreePanel} tree
36634 * @param {Object} config (optional)
36635  */
36636 Roo.tree.TreeFilter = function(tree, config){
36637     this.tree = tree;
36638     this.filtered = {};
36639     Roo.apply(this, config);
36640 };
36641
36642 Roo.tree.TreeFilter.prototype = {
36643     clearBlank:false,
36644     reverse:false,
36645     autoClear:false,
36646     remove:false,
36647
36648      /**
36649      * Filter the data by a specific attribute.
36650      * @param {String/RegExp} value Either string that the attribute value
36651      * should start with or a RegExp to test against the attribute
36652      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36653      * @param {TreeNode} startNode (optional) The node to start the filter at.
36654      */
36655     filter : function(value, attr, startNode){
36656         attr = attr || "text";
36657         var f;
36658         if(typeof value == "string"){
36659             var vlen = value.length;
36660             // auto clear empty filter
36661             if(vlen == 0 && this.clearBlank){
36662                 this.clear();
36663                 return;
36664             }
36665             value = value.toLowerCase();
36666             f = function(n){
36667                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36668             };
36669         }else if(value.exec){ // regex?
36670             f = function(n){
36671                 return value.test(n.attributes[attr]);
36672             };
36673         }else{
36674             throw 'Illegal filter type, must be string or regex';
36675         }
36676         this.filterBy(f, null, startNode);
36677         },
36678
36679     /**
36680      * Filter by a function. The passed function will be called with each
36681      * node in the tree (or from the startNode). If the function returns true, the node is kept
36682      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36683      * @param {Function} fn The filter function
36684      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36685      */
36686     filterBy : function(fn, scope, startNode){
36687         startNode = startNode || this.tree.root;
36688         if(this.autoClear){
36689             this.clear();
36690         }
36691         var af = this.filtered, rv = this.reverse;
36692         var f = function(n){
36693             if(n == startNode){
36694                 return true;
36695             }
36696             if(af[n.id]){
36697                 return false;
36698             }
36699             var m = fn.call(scope || n, n);
36700             if(!m || rv){
36701                 af[n.id] = n;
36702                 n.ui.hide();
36703                 return false;
36704             }
36705             return true;
36706         };
36707         startNode.cascade(f);
36708         if(this.remove){
36709            for(var id in af){
36710                if(typeof id != "function"){
36711                    var n = af[id];
36712                    if(n && n.parentNode){
36713                        n.parentNode.removeChild(n);
36714                    }
36715                }
36716            }
36717         }
36718     },
36719
36720     /**
36721      * Clears the current filter. Note: with the "remove" option
36722      * set a filter cannot be cleared.
36723      */
36724     clear : function(){
36725         var t = this.tree;
36726         var af = this.filtered;
36727         for(var id in af){
36728             if(typeof id != "function"){
36729                 var n = af[id];
36730                 if(n){
36731                     n.ui.show();
36732                 }
36733             }
36734         }
36735         this.filtered = {};
36736     }
36737 };
36738 /*
36739  * Based on:
36740  * Ext JS Library 1.1.1
36741  * Copyright(c) 2006-2007, Ext JS, LLC.
36742  *
36743  * Originally Released Under LGPL - original licence link has changed is not relivant.
36744  *
36745  * Fork - LGPL
36746  * <script type="text/javascript">
36747  */
36748  
36749
36750 /**
36751  * @class Roo.tree.TreeSorter
36752  * Provides sorting of nodes in a TreePanel
36753  * 
36754  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36755  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36756  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36757  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36758  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36759  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36760  * @constructor
36761  * @param {TreePanel} tree
36762  * @param {Object} config
36763  */
36764 Roo.tree.TreeSorter = function(tree, config){
36765     Roo.apply(this, config);
36766     tree.on("beforechildrenrendered", this.doSort, this);
36767     tree.on("append", this.updateSort, this);
36768     tree.on("insert", this.updateSort, this);
36769     
36770     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36771     var p = this.property || "text";
36772     var sortType = this.sortType;
36773     var fs = this.folderSort;
36774     var cs = this.caseSensitive === true;
36775     var leafAttr = this.leafAttr || 'leaf';
36776
36777     this.sortFn = function(n1, n2){
36778         if(fs){
36779             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36780                 return 1;
36781             }
36782             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36783                 return -1;
36784             }
36785         }
36786         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36787         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36788         if(v1 < v2){
36789                         return dsc ? +1 : -1;
36790                 }else if(v1 > v2){
36791                         return dsc ? -1 : +1;
36792         }else{
36793                 return 0;
36794         }
36795     };
36796 };
36797
36798 Roo.tree.TreeSorter.prototype = {
36799     doSort : function(node){
36800         node.sort(this.sortFn);
36801     },
36802     
36803     compareNodes : function(n1, n2){
36804         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36805     },
36806     
36807     updateSort : function(tree, node){
36808         if(node.childrenRendered){
36809             this.doSort.defer(1, this, [node]);
36810         }
36811     }
36812 };/*
36813  * Based on:
36814  * Ext JS Library 1.1.1
36815  * Copyright(c) 2006-2007, Ext JS, LLC.
36816  *
36817  * Originally Released Under LGPL - original licence link has changed is not relivant.
36818  *
36819  * Fork - LGPL
36820  * <script type="text/javascript">
36821  */
36822
36823 if(Roo.dd.DropZone){
36824     
36825 Roo.tree.TreeDropZone = function(tree, config){
36826     this.allowParentInsert = false;
36827     this.allowContainerDrop = false;
36828     this.appendOnly = false;
36829     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36830     this.tree = tree;
36831     this.lastInsertClass = "x-tree-no-status";
36832     this.dragOverData = {};
36833 };
36834
36835 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36836     ddGroup : "TreeDD",
36837     scroll:  true,
36838     
36839     expandDelay : 1000,
36840     
36841     expandNode : function(node){
36842         if(node.hasChildNodes() && !node.isExpanded()){
36843             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36844         }
36845     },
36846     
36847     queueExpand : function(node){
36848         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36849     },
36850     
36851     cancelExpand : function(){
36852         if(this.expandProcId){
36853             clearTimeout(this.expandProcId);
36854             this.expandProcId = false;
36855         }
36856     },
36857     
36858     isValidDropPoint : function(n, pt, dd, e, data){
36859         if(!n || !data){ return false; }
36860         var targetNode = n.node;
36861         var dropNode = data.node;
36862         // default drop rules
36863         if(!(targetNode && targetNode.isTarget && pt)){
36864             return false;
36865         }
36866         if(pt == "append" && targetNode.allowChildren === false){
36867             return false;
36868         }
36869         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36870             return false;
36871         }
36872         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36873             return false;
36874         }
36875         // reuse the object
36876         var overEvent = this.dragOverData;
36877         overEvent.tree = this.tree;
36878         overEvent.target = targetNode;
36879         overEvent.data = data;
36880         overEvent.point = pt;
36881         overEvent.source = dd;
36882         overEvent.rawEvent = e;
36883         overEvent.dropNode = dropNode;
36884         overEvent.cancel = false;  
36885         var result = this.tree.fireEvent("nodedragover", overEvent);
36886         return overEvent.cancel === false && result !== false;
36887     },
36888     
36889     getDropPoint : function(e, n, dd)
36890     {
36891         var tn = n.node;
36892         if(tn.isRoot){
36893             return tn.allowChildren !== false ? "append" : false; // always append for root
36894         }
36895         var dragEl = n.ddel;
36896         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36897         var y = Roo.lib.Event.getPageY(e);
36898         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36899         
36900         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36901         var noAppend = tn.allowChildren === false;
36902         if(this.appendOnly || tn.parentNode.allowChildren === false){
36903             return noAppend ? false : "append";
36904         }
36905         var noBelow = false;
36906         if(!this.allowParentInsert){
36907             noBelow = tn.hasChildNodes() && tn.isExpanded();
36908         }
36909         var q = (b - t) / (noAppend ? 2 : 3);
36910         if(y >= t && y < (t + q)){
36911             return "above";
36912         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36913             return "below";
36914         }else{
36915             return "append";
36916         }
36917     },
36918     
36919     onNodeEnter : function(n, dd, e, data)
36920     {
36921         this.cancelExpand();
36922     },
36923     
36924     onNodeOver : function(n, dd, e, data)
36925     {
36926        
36927         var pt = this.getDropPoint(e, n, dd);
36928         var node = n.node;
36929         
36930         // auto node expand check
36931         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36932             this.queueExpand(node);
36933         }else if(pt != "append"){
36934             this.cancelExpand();
36935         }
36936         
36937         // set the insert point style on the target node
36938         var returnCls = this.dropNotAllowed;
36939         if(this.isValidDropPoint(n, pt, dd, e, data)){
36940            if(pt){
36941                var el = n.ddel;
36942                var cls;
36943                if(pt == "above"){
36944                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36945                    cls = "x-tree-drag-insert-above";
36946                }else if(pt == "below"){
36947                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36948                    cls = "x-tree-drag-insert-below";
36949                }else{
36950                    returnCls = "x-tree-drop-ok-append";
36951                    cls = "x-tree-drag-append";
36952                }
36953                if(this.lastInsertClass != cls){
36954                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36955                    this.lastInsertClass = cls;
36956                }
36957            }
36958        }
36959        return returnCls;
36960     },
36961     
36962     onNodeOut : function(n, dd, e, data){
36963         
36964         this.cancelExpand();
36965         this.removeDropIndicators(n);
36966     },
36967     
36968     onNodeDrop : function(n, dd, e, data){
36969         var point = this.getDropPoint(e, n, dd);
36970         var targetNode = n.node;
36971         targetNode.ui.startDrop();
36972         if(!this.isValidDropPoint(n, point, dd, e, data)){
36973             targetNode.ui.endDrop();
36974             return false;
36975         }
36976         // first try to find the drop node
36977         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36978         var dropEvent = {
36979             tree : this.tree,
36980             target: targetNode,
36981             data: data,
36982             point: point,
36983             source: dd,
36984             rawEvent: e,
36985             dropNode: dropNode,
36986             cancel: !dropNode   
36987         };
36988         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36989         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36990             targetNode.ui.endDrop();
36991             return false;
36992         }
36993         // allow target changing
36994         targetNode = dropEvent.target;
36995         if(point == "append" && !targetNode.isExpanded()){
36996             targetNode.expand(false, null, function(){
36997                 this.completeDrop(dropEvent);
36998             }.createDelegate(this));
36999         }else{
37000             this.completeDrop(dropEvent);
37001         }
37002         return true;
37003     },
37004     
37005     completeDrop : function(de){
37006         var ns = de.dropNode, p = de.point, t = de.target;
37007         if(!(ns instanceof Array)){
37008             ns = [ns];
37009         }
37010         var n;
37011         for(var i = 0, len = ns.length; i < len; i++){
37012             n = ns[i];
37013             if(p == "above"){
37014                 t.parentNode.insertBefore(n, t);
37015             }else if(p == "below"){
37016                 t.parentNode.insertBefore(n, t.nextSibling);
37017             }else{
37018                 t.appendChild(n);
37019             }
37020         }
37021         n.ui.focus();
37022         if(this.tree.hlDrop){
37023             n.ui.highlight();
37024         }
37025         t.ui.endDrop();
37026         this.tree.fireEvent("nodedrop", de);
37027     },
37028     
37029     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37030         if(this.tree.hlDrop){
37031             dropNode.ui.focus();
37032             dropNode.ui.highlight();
37033         }
37034         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37035     },
37036     
37037     getTree : function(){
37038         return this.tree;
37039     },
37040     
37041     removeDropIndicators : function(n){
37042         if(n && n.ddel){
37043             var el = n.ddel;
37044             Roo.fly(el).removeClass([
37045                     "x-tree-drag-insert-above",
37046                     "x-tree-drag-insert-below",
37047                     "x-tree-drag-append"]);
37048             this.lastInsertClass = "_noclass";
37049         }
37050     },
37051     
37052     beforeDragDrop : function(target, e, id){
37053         this.cancelExpand();
37054         return true;
37055     },
37056     
37057     afterRepair : function(data){
37058         if(data && Roo.enableFx){
37059             data.node.ui.highlight();
37060         }
37061         this.hideProxy();
37062     } 
37063     
37064 });
37065
37066 }
37067 /*
37068  * Based on:
37069  * Ext JS Library 1.1.1
37070  * Copyright(c) 2006-2007, Ext JS, LLC.
37071  *
37072  * Originally Released Under LGPL - original licence link has changed is not relivant.
37073  *
37074  * Fork - LGPL
37075  * <script type="text/javascript">
37076  */
37077  
37078
37079 if(Roo.dd.DragZone){
37080 Roo.tree.TreeDragZone = function(tree, config){
37081     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37082     this.tree = tree;
37083 };
37084
37085 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37086     ddGroup : "TreeDD",
37087    
37088     onBeforeDrag : function(data, e){
37089         var n = data.node;
37090         return n && n.draggable && !n.disabled;
37091     },
37092      
37093     
37094     onInitDrag : function(e){
37095         var data = this.dragData;
37096         this.tree.getSelectionModel().select(data.node);
37097         this.proxy.update("");
37098         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37099         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37100     },
37101     
37102     getRepairXY : function(e, data){
37103         return data.node.ui.getDDRepairXY();
37104     },
37105     
37106     onEndDrag : function(data, e){
37107         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37108         
37109         
37110     },
37111     
37112     onValidDrop : function(dd, e, id){
37113         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37114         this.hideProxy();
37115     },
37116     
37117     beforeInvalidDrop : function(e, id){
37118         // this scrolls the original position back into view
37119         var sm = this.tree.getSelectionModel();
37120         sm.clearSelections();
37121         sm.select(this.dragData.node);
37122     }
37123 });
37124 }/*
37125  * Based on:
37126  * Ext JS Library 1.1.1
37127  * Copyright(c) 2006-2007, Ext JS, LLC.
37128  *
37129  * Originally Released Under LGPL - original licence link has changed is not relivant.
37130  *
37131  * Fork - LGPL
37132  * <script type="text/javascript">
37133  */
37134 /**
37135  * @class Roo.tree.TreeEditor
37136  * @extends Roo.Editor
37137  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37138  * as the editor field.
37139  * @constructor
37140  * @param {Object} config (used to be the tree panel.)
37141  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37142  * 
37143  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37144  * @cfg {Roo.form.TextField} field [required] The field configuration
37145  *
37146  * 
37147  */
37148 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37149     var tree = config;
37150     var field;
37151     if (oldconfig) { // old style..
37152         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37153     } else {
37154         // new style..
37155         tree = config.tree;
37156         config.field = config.field  || {};
37157         config.field.xtype = 'TextField';
37158         field = Roo.factory(config.field, Roo.form);
37159     }
37160     config = config || {};
37161     
37162     
37163     this.addEvents({
37164         /**
37165          * @event beforenodeedit
37166          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37167          * false from the handler of this event.
37168          * @param {Editor} this
37169          * @param {Roo.tree.Node} node 
37170          */
37171         "beforenodeedit" : true
37172     });
37173     
37174     //Roo.log(config);
37175     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37176
37177     this.tree = tree;
37178
37179     tree.on('beforeclick', this.beforeNodeClick, this);
37180     tree.getTreeEl().on('mousedown', this.hide, this);
37181     this.on('complete', this.updateNode, this);
37182     this.on('beforestartedit', this.fitToTree, this);
37183     this.on('startedit', this.bindScroll, this, {delay:10});
37184     this.on('specialkey', this.onSpecialKey, this);
37185 };
37186
37187 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37188     /**
37189      * @cfg {String} alignment
37190      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37191      */
37192     alignment: "l-l",
37193     // inherit
37194     autoSize: false,
37195     /**
37196      * @cfg {Boolean} hideEl
37197      * True to hide the bound element while the editor is displayed (defaults to false)
37198      */
37199     hideEl : false,
37200     /**
37201      * @cfg {String} cls
37202      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37203      */
37204     cls: "x-small-editor x-tree-editor",
37205     /**
37206      * @cfg {Boolean} shim
37207      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37208      */
37209     shim:false,
37210     // inherit
37211     shadow:"frame",
37212     /**
37213      * @cfg {Number} maxWidth
37214      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37215      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37216      * scroll and client offsets into account prior to each edit.
37217      */
37218     maxWidth: 250,
37219
37220     editDelay : 350,
37221
37222     // private
37223     fitToTree : function(ed, el){
37224         var td = this.tree.getTreeEl().dom, nd = el.dom;
37225         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37226             td.scrollLeft = nd.offsetLeft;
37227         }
37228         var w = Math.min(
37229                 this.maxWidth,
37230                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37231         this.setSize(w, '');
37232         
37233         return this.fireEvent('beforenodeedit', this, this.editNode);
37234         
37235     },
37236
37237     // private
37238     triggerEdit : function(node){
37239         this.completeEdit();
37240         this.editNode = node;
37241         this.startEdit(node.ui.textNode, node.text);
37242     },
37243
37244     // private
37245     bindScroll : function(){
37246         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37247     },
37248
37249     // private
37250     beforeNodeClick : function(node, e){
37251         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37252         this.lastClick = new Date();
37253         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37254             e.stopEvent();
37255             this.triggerEdit(node);
37256             return false;
37257         }
37258         return true;
37259     },
37260
37261     // private
37262     updateNode : function(ed, value){
37263         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37264         this.editNode.setText(value);
37265     },
37266
37267     // private
37268     onHide : function(){
37269         Roo.tree.TreeEditor.superclass.onHide.call(this);
37270         if(this.editNode){
37271             this.editNode.ui.focus();
37272         }
37273     },
37274
37275     // private
37276     onSpecialKey : function(field, e){
37277         var k = e.getKey();
37278         if(k == e.ESC){
37279             e.stopEvent();
37280             this.cancelEdit();
37281         }else if(k == e.ENTER && !e.hasModifier()){
37282             e.stopEvent();
37283             this.completeEdit();
37284         }
37285     }
37286 });//<Script type="text/javascript">
37287 /*
37288  * Based on:
37289  * Ext JS Library 1.1.1
37290  * Copyright(c) 2006-2007, Ext JS, LLC.
37291  *
37292  * Originally Released Under LGPL - original licence link has changed is not relivant.
37293  *
37294  * Fork - LGPL
37295  * <script type="text/javascript">
37296  */
37297  
37298 /**
37299  * Not documented??? - probably should be...
37300  */
37301
37302 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37303     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37304     
37305     renderElements : function(n, a, targetNode, bulkRender){
37306         //consel.log("renderElements?");
37307         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37308
37309         var t = n.getOwnerTree();
37310         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37311         
37312         var cols = t.columns;
37313         var bw = t.borderWidth;
37314         var c = cols[0];
37315         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37316          var cb = typeof a.checked == "boolean";
37317         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37318         var colcls = 'x-t-' + tid + '-c0';
37319         var buf = [
37320             '<li class="x-tree-node">',
37321             
37322                 
37323                 '<div class="x-tree-node-el ', a.cls,'">',
37324                     // extran...
37325                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37326                 
37327                 
37328                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37329                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37330                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37331                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37332                            (a.iconCls ? ' '+a.iconCls : ''),
37333                            '" unselectable="on" />',
37334                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37335                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37336                              
37337                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37338                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37339                             '<span unselectable="on" qtip="' + tx + '">',
37340                              tx,
37341                              '</span></a>' ,
37342                     '</div>',
37343                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37344                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37345                  ];
37346         for(var i = 1, len = cols.length; i < len; i++){
37347             c = cols[i];
37348             colcls = 'x-t-' + tid + '-c' +i;
37349             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37350             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37351                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37352                       "</div>");
37353          }
37354          
37355          buf.push(
37356             '</a>',
37357             '<div class="x-clear"></div></div>',
37358             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37359             "</li>");
37360         
37361         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37362             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37363                                 n.nextSibling.ui.getEl(), buf.join(""));
37364         }else{
37365             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37366         }
37367         var el = this.wrap.firstChild;
37368         this.elRow = el;
37369         this.elNode = el.firstChild;
37370         this.ranchor = el.childNodes[1];
37371         this.ctNode = this.wrap.childNodes[1];
37372         var cs = el.firstChild.childNodes;
37373         this.indentNode = cs[0];
37374         this.ecNode = cs[1];
37375         this.iconNode = cs[2];
37376         var index = 3;
37377         if(cb){
37378             this.checkbox = cs[3];
37379             index++;
37380         }
37381         this.anchor = cs[index];
37382         
37383         this.textNode = cs[index].firstChild;
37384         
37385         //el.on("click", this.onClick, this);
37386         //el.on("dblclick", this.onDblClick, this);
37387         
37388         
37389        // console.log(this);
37390     },
37391     initEvents : function(){
37392         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37393         
37394             
37395         var a = this.ranchor;
37396
37397         var el = Roo.get(a);
37398
37399         if(Roo.isOpera){ // opera render bug ignores the CSS
37400             el.setStyle("text-decoration", "none");
37401         }
37402
37403         el.on("click", this.onClick, this);
37404         el.on("dblclick", this.onDblClick, this);
37405         el.on("contextmenu", this.onContextMenu, this);
37406         
37407     },
37408     
37409     /*onSelectedChange : function(state){
37410         if(state){
37411             this.focus();
37412             this.addClass("x-tree-selected");
37413         }else{
37414             //this.blur();
37415             this.removeClass("x-tree-selected");
37416         }
37417     },*/
37418     addClass : function(cls){
37419         if(this.elRow){
37420             Roo.fly(this.elRow).addClass(cls);
37421         }
37422         
37423     },
37424     
37425     
37426     removeClass : function(cls){
37427         if(this.elRow){
37428             Roo.fly(this.elRow).removeClass(cls);
37429         }
37430     }
37431
37432     
37433     
37434 });//<Script type="text/javascript">
37435
37436 /*
37437  * Based on:
37438  * Ext JS Library 1.1.1
37439  * Copyright(c) 2006-2007, Ext JS, LLC.
37440  *
37441  * Originally Released Under LGPL - original licence link has changed is not relivant.
37442  *
37443  * Fork - LGPL
37444  * <script type="text/javascript">
37445  */
37446  
37447
37448 /**
37449  * @class Roo.tree.ColumnTree
37450  * @extends Roo.data.TreePanel
37451  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37452  * @cfg {int} borderWidth  compined right/left border allowance
37453  * @constructor
37454  * @param {String/HTMLElement/Element} el The container element
37455  * @param {Object} config
37456  */
37457 Roo.tree.ColumnTree =  function(el, config)
37458 {
37459    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37460    this.addEvents({
37461         /**
37462         * @event resize
37463         * Fire this event on a container when it resizes
37464         * @param {int} w Width
37465         * @param {int} h Height
37466         */
37467        "resize" : true
37468     });
37469     this.on('resize', this.onResize, this);
37470 };
37471
37472 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37473     //lines:false,
37474     
37475     
37476     borderWidth: Roo.isBorderBox ? 0 : 2, 
37477     headEls : false,
37478     
37479     render : function(){
37480         // add the header.....
37481        
37482         Roo.tree.ColumnTree.superclass.render.apply(this);
37483         
37484         this.el.addClass('x-column-tree');
37485         
37486         this.headers = this.el.createChild(
37487             {cls:'x-tree-headers'},this.innerCt.dom);
37488    
37489         var cols = this.columns, c;
37490         var totalWidth = 0;
37491         this.headEls = [];
37492         var  len = cols.length;
37493         for(var i = 0; i < len; i++){
37494              c = cols[i];
37495              totalWidth += c.width;
37496             this.headEls.push(this.headers.createChild({
37497                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37498                  cn: {
37499                      cls:'x-tree-hd-text',
37500                      html: c.header
37501                  },
37502                  style:'width:'+(c.width-this.borderWidth)+'px;'
37503              }));
37504         }
37505         this.headers.createChild({cls:'x-clear'});
37506         // prevent floats from wrapping when clipped
37507         this.headers.setWidth(totalWidth);
37508         //this.innerCt.setWidth(totalWidth);
37509         this.innerCt.setStyle({ overflow: 'auto' });
37510         this.onResize(this.width, this.height);
37511              
37512         
37513     },
37514     onResize : function(w,h)
37515     {
37516         this.height = h;
37517         this.width = w;
37518         // resize cols..
37519         this.innerCt.setWidth(this.width);
37520         this.innerCt.setHeight(this.height-20);
37521         
37522         // headers...
37523         var cols = this.columns, c;
37524         var totalWidth = 0;
37525         var expEl = false;
37526         var len = cols.length;
37527         for(var i = 0; i < len; i++){
37528             c = cols[i];
37529             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37530                 // it's the expander..
37531                 expEl  = this.headEls[i];
37532                 continue;
37533             }
37534             totalWidth += c.width;
37535             
37536         }
37537         if (expEl) {
37538             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37539         }
37540         this.headers.setWidth(w-20);
37541
37542         
37543         
37544         
37545     }
37546 });
37547 /*
37548  * Based on:
37549  * Ext JS Library 1.1.1
37550  * Copyright(c) 2006-2007, Ext JS, LLC.
37551  *
37552  * Originally Released Under LGPL - original licence link has changed is not relivant.
37553  *
37554  * Fork - LGPL
37555  * <script type="text/javascript">
37556  */
37557  
37558 /**
37559  * @class Roo.menu.Menu
37560  * @extends Roo.util.Observable
37561  * @children Roo.menu.BaseItem
37562  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37563  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37564  * @constructor
37565  * Creates a new Menu
37566  * @param {Object} config Configuration options
37567  */
37568 Roo.menu.Menu = function(config){
37569     
37570     Roo.menu.Menu.superclass.constructor.call(this, config);
37571     
37572     this.id = this.id || Roo.id();
37573     this.addEvents({
37574         /**
37575          * @event beforeshow
37576          * Fires before this menu is displayed
37577          * @param {Roo.menu.Menu} this
37578          */
37579         beforeshow : true,
37580         /**
37581          * @event beforehide
37582          * Fires before this menu is hidden
37583          * @param {Roo.menu.Menu} this
37584          */
37585         beforehide : true,
37586         /**
37587          * @event show
37588          * Fires after this menu is displayed
37589          * @param {Roo.menu.Menu} this
37590          */
37591         show : true,
37592         /**
37593          * @event hide
37594          * Fires after this menu is hidden
37595          * @param {Roo.menu.Menu} this
37596          */
37597         hide : true,
37598         /**
37599          * @event click
37600          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37601          * @param {Roo.menu.Menu} this
37602          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37603          * @param {Roo.EventObject} e
37604          */
37605         click : true,
37606         /**
37607          * @event mouseover
37608          * Fires when the mouse is hovering over this menu
37609          * @param {Roo.menu.Menu} this
37610          * @param {Roo.EventObject} e
37611          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37612          */
37613         mouseover : true,
37614         /**
37615          * @event mouseout
37616          * Fires when the mouse exits this menu
37617          * @param {Roo.menu.Menu} this
37618          * @param {Roo.EventObject} e
37619          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37620          */
37621         mouseout : true,
37622         /**
37623          * @event itemclick
37624          * Fires when a menu item contained in this menu is clicked
37625          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37626          * @param {Roo.EventObject} e
37627          */
37628         itemclick: true
37629     });
37630     if (this.registerMenu) {
37631         Roo.menu.MenuMgr.register(this);
37632     }
37633     
37634     var mis = this.items;
37635     this.items = new Roo.util.MixedCollection();
37636     if(mis){
37637         this.add.apply(this, mis);
37638     }
37639 };
37640
37641 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37642     /**
37643      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37644      */
37645     minWidth : 120,
37646     /**
37647      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37648      * for bottom-right shadow (defaults to "sides")
37649      */
37650     shadow : "sides",
37651     /**
37652      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37653      * this menu (defaults to "tl-tr?")
37654      */
37655     subMenuAlign : "tl-tr?",
37656     /**
37657      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37658      * relative to its element of origin (defaults to "tl-bl?")
37659      */
37660     defaultAlign : "tl-bl?",
37661     /**
37662      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37663      */
37664     allowOtherMenus : false,
37665     /**
37666      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37667      */
37668     registerMenu : true,
37669
37670     hidden:true,
37671
37672     // private
37673     render : function(){
37674         if(this.el){
37675             return;
37676         }
37677         var el = this.el = new Roo.Layer({
37678             cls: "x-menu",
37679             shadow:this.shadow,
37680             constrain: false,
37681             parentEl: this.parentEl || document.body,
37682             zindex:15000
37683         });
37684
37685         this.keyNav = new Roo.menu.MenuNav(this);
37686
37687         if(this.plain){
37688             el.addClass("x-menu-plain");
37689         }
37690         if(this.cls){
37691             el.addClass(this.cls);
37692         }
37693         // generic focus element
37694         this.focusEl = el.createChild({
37695             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37696         });
37697         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37698         //disabling touch- as it's causing issues ..
37699         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37700         ul.on('click'   , this.onClick, this);
37701         
37702         
37703         ul.on("mouseover", this.onMouseOver, this);
37704         ul.on("mouseout", this.onMouseOut, this);
37705         this.items.each(function(item){
37706             if (item.hidden) {
37707                 return;
37708             }
37709             
37710             var li = document.createElement("li");
37711             li.className = "x-menu-list-item";
37712             ul.dom.appendChild(li);
37713             item.render(li, this);
37714         }, this);
37715         this.ul = ul;
37716         this.autoWidth();
37717     },
37718
37719     // private
37720     autoWidth : function(){
37721         var el = this.el, ul = this.ul;
37722         if(!el){
37723             return;
37724         }
37725         var w = this.width;
37726         if(w){
37727             el.setWidth(w);
37728         }else if(Roo.isIE){
37729             el.setWidth(this.minWidth);
37730             var t = el.dom.offsetWidth; // force recalc
37731             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37732         }
37733     },
37734
37735     // private
37736     delayAutoWidth : function(){
37737         if(this.rendered){
37738             if(!this.awTask){
37739                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37740             }
37741             this.awTask.delay(20);
37742         }
37743     },
37744
37745     // private
37746     findTargetItem : function(e){
37747         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37748         if(t && t.menuItemId){
37749             return this.items.get(t.menuItemId);
37750         }
37751     },
37752
37753     // private
37754     onClick : function(e){
37755         Roo.log("menu.onClick");
37756         var t = this.findTargetItem(e);
37757         if(!t){
37758             return;
37759         }
37760         Roo.log(e);
37761         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37762             if(t == this.activeItem && t.shouldDeactivate(e)){
37763                 this.activeItem.deactivate();
37764                 delete this.activeItem;
37765                 return;
37766             }
37767             if(t.canActivate){
37768                 this.setActiveItem(t, true);
37769             }
37770             return;
37771             
37772             
37773         }
37774         
37775         t.onClick(e);
37776         this.fireEvent("click", this, t, e);
37777     },
37778
37779     // private
37780     setActiveItem : function(item, autoExpand){
37781         if(item != this.activeItem){
37782             if(this.activeItem){
37783                 this.activeItem.deactivate();
37784             }
37785             this.activeItem = item;
37786             item.activate(autoExpand);
37787         }else if(autoExpand){
37788             item.expandMenu();
37789         }
37790     },
37791
37792     // private
37793     tryActivate : function(start, step){
37794         var items = this.items;
37795         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37796             var item = items.get(i);
37797             if(!item.disabled && item.canActivate){
37798                 this.setActiveItem(item, false);
37799                 return item;
37800             }
37801         }
37802         return false;
37803     },
37804
37805     // private
37806     onMouseOver : function(e){
37807         var t;
37808         if(t = this.findTargetItem(e)){
37809             if(t.canActivate && !t.disabled){
37810                 this.setActiveItem(t, true);
37811             }
37812         }
37813         this.fireEvent("mouseover", this, e, t);
37814     },
37815
37816     // private
37817     onMouseOut : function(e){
37818         var t;
37819         if(t = this.findTargetItem(e)){
37820             if(t == this.activeItem && t.shouldDeactivate(e)){
37821                 this.activeItem.deactivate();
37822                 delete this.activeItem;
37823             }
37824         }
37825         this.fireEvent("mouseout", this, e, t);
37826     },
37827
37828     /**
37829      * Read-only.  Returns true if the menu is currently displayed, else false.
37830      * @type Boolean
37831      */
37832     isVisible : function(){
37833         return this.el && !this.hidden;
37834     },
37835
37836     /**
37837      * Displays this menu relative to another element
37838      * @param {String/HTMLElement/Roo.Element} element The element to align to
37839      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37840      * the element (defaults to this.defaultAlign)
37841      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37842      */
37843     show : function(el, pos, parentMenu){
37844         this.parentMenu = parentMenu;
37845         if(!this.el){
37846             this.render();
37847         }
37848         this.fireEvent("beforeshow", this);
37849         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37850     },
37851
37852     /**
37853      * Displays this menu at a specific xy position
37854      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37855      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37856      */
37857     showAt : function(xy, parentMenu, /* private: */_e){
37858         this.parentMenu = parentMenu;
37859         if(!this.el){
37860             this.render();
37861         }
37862         if(_e !== false){
37863             this.fireEvent("beforeshow", this);
37864             xy = this.el.adjustForConstraints(xy);
37865         }
37866         this.el.setXY(xy);
37867         this.el.show();
37868         this.hidden = false;
37869         this.focus();
37870         this.fireEvent("show", this);
37871     },
37872
37873     focus : function(){
37874         if(!this.hidden){
37875             this.doFocus.defer(50, this);
37876         }
37877     },
37878
37879     doFocus : function(){
37880         if(!this.hidden){
37881             this.focusEl.focus();
37882         }
37883     },
37884
37885     /**
37886      * Hides this menu and optionally all parent menus
37887      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37888      */
37889     hide : function(deep){
37890         if(this.el && this.isVisible()){
37891             this.fireEvent("beforehide", this);
37892             if(this.activeItem){
37893                 this.activeItem.deactivate();
37894                 this.activeItem = null;
37895             }
37896             this.el.hide();
37897             this.hidden = true;
37898             this.fireEvent("hide", this);
37899         }
37900         if(deep === true && this.parentMenu){
37901             this.parentMenu.hide(true);
37902         }
37903     },
37904
37905     /**
37906      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37907      * Any of the following are valid:
37908      * <ul>
37909      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37910      * <li>An HTMLElement object which will be converted to a menu item</li>
37911      * <li>A menu item config object that will be created as a new menu item</li>
37912      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37913      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37914      * </ul>
37915      * Usage:
37916      * <pre><code>
37917 // Create the menu
37918 var menu = new Roo.menu.Menu();
37919
37920 // Create a menu item to add by reference
37921 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37922
37923 // Add a bunch of items at once using different methods.
37924 // Only the last item added will be returned.
37925 var item = menu.add(
37926     menuItem,                // add existing item by ref
37927     'Dynamic Item',          // new TextItem
37928     '-',                     // new separator
37929     { text: 'Config Item' }  // new item by config
37930 );
37931 </code></pre>
37932      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37933      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37934      */
37935     add : function(){
37936         var a = arguments, l = a.length, item;
37937         for(var i = 0; i < l; i++){
37938             var el = a[i];
37939             if ((typeof(el) == "object") && el.xtype && el.xns) {
37940                 el = Roo.factory(el, Roo.menu);
37941             }
37942             
37943             if(el.render){ // some kind of Item
37944                 item = this.addItem(el);
37945             }else if(typeof el == "string"){ // string
37946                 if(el == "separator" || el == "-"){
37947                     item = this.addSeparator();
37948                 }else{
37949                     item = this.addText(el);
37950                 }
37951             }else if(el.tagName || el.el){ // element
37952                 item = this.addElement(el);
37953             }else if(typeof el == "object"){ // must be menu item config?
37954                 item = this.addMenuItem(el);
37955             }
37956         }
37957         return item;
37958     },
37959
37960     /**
37961      * Returns this menu's underlying {@link Roo.Element} object
37962      * @return {Roo.Element} The element
37963      */
37964     getEl : function(){
37965         if(!this.el){
37966             this.render();
37967         }
37968         return this.el;
37969     },
37970
37971     /**
37972      * Adds a separator bar to the menu
37973      * @return {Roo.menu.Item} The menu item that was added
37974      */
37975     addSeparator : function(){
37976         return this.addItem(new Roo.menu.Separator());
37977     },
37978
37979     /**
37980      * Adds an {@link Roo.Element} object to the menu
37981      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37982      * @return {Roo.menu.Item} The menu item that was added
37983      */
37984     addElement : function(el){
37985         return this.addItem(new Roo.menu.BaseItem(el));
37986     },
37987
37988     /**
37989      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37990      * @param {Roo.menu.Item} item The menu item to add
37991      * @return {Roo.menu.Item} The menu item that was added
37992      */
37993     addItem : function(item){
37994         this.items.add(item);
37995         if(this.ul){
37996             var li = document.createElement("li");
37997             li.className = "x-menu-list-item";
37998             this.ul.dom.appendChild(li);
37999             item.render(li, this);
38000             this.delayAutoWidth();
38001         }
38002         return item;
38003     },
38004
38005     /**
38006      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38007      * @param {Object} config A MenuItem config object
38008      * @return {Roo.menu.Item} The menu item that was added
38009      */
38010     addMenuItem : function(config){
38011         if(!(config instanceof Roo.menu.Item)){
38012             if(typeof config.checked == "boolean"){ // must be check menu item config?
38013                 config = new Roo.menu.CheckItem(config);
38014             }else{
38015                 config = new Roo.menu.Item(config);
38016             }
38017         }
38018         return this.addItem(config);
38019     },
38020
38021     /**
38022      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38023      * @param {String} text The text to display in the menu item
38024      * @return {Roo.menu.Item} The menu item that was added
38025      */
38026     addText : function(text){
38027         return this.addItem(new Roo.menu.TextItem({ text : text }));
38028     },
38029
38030     /**
38031      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38032      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38033      * @param {Roo.menu.Item} item The menu item to add
38034      * @return {Roo.menu.Item} The menu item that was added
38035      */
38036     insert : function(index, item){
38037         this.items.insert(index, item);
38038         if(this.ul){
38039             var li = document.createElement("li");
38040             li.className = "x-menu-list-item";
38041             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38042             item.render(li, this);
38043             this.delayAutoWidth();
38044         }
38045         return item;
38046     },
38047
38048     /**
38049      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38050      * @param {Roo.menu.Item} item The menu item to remove
38051      */
38052     remove : function(item){
38053         this.items.removeKey(item.id);
38054         item.destroy();
38055     },
38056
38057     /**
38058      * Removes and destroys all items in the menu
38059      */
38060     removeAll : function(){
38061         var f;
38062         while(f = this.items.first()){
38063             this.remove(f);
38064         }
38065     }
38066 });
38067
38068 // MenuNav is a private utility class used internally by the Menu
38069 Roo.menu.MenuNav = function(menu){
38070     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38071     this.scope = this.menu = menu;
38072 };
38073
38074 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38075     doRelay : function(e, h){
38076         var k = e.getKey();
38077         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38078             this.menu.tryActivate(0, 1);
38079             return false;
38080         }
38081         return h.call(this.scope || this, e, this.menu);
38082     },
38083
38084     up : function(e, m){
38085         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38086             m.tryActivate(m.items.length-1, -1);
38087         }
38088     },
38089
38090     down : function(e, m){
38091         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38092             m.tryActivate(0, 1);
38093         }
38094     },
38095
38096     right : function(e, m){
38097         if(m.activeItem){
38098             m.activeItem.expandMenu(true);
38099         }
38100     },
38101
38102     left : function(e, m){
38103         m.hide();
38104         if(m.parentMenu && m.parentMenu.activeItem){
38105             m.parentMenu.activeItem.activate();
38106         }
38107     },
38108
38109     enter : function(e, m){
38110         if(m.activeItem){
38111             e.stopPropagation();
38112             m.activeItem.onClick(e);
38113             m.fireEvent("click", this, m.activeItem);
38114             return true;
38115         }
38116     }
38117 });/*
38118  * Based on:
38119  * Ext JS Library 1.1.1
38120  * Copyright(c) 2006-2007, Ext JS, LLC.
38121  *
38122  * Originally Released Under LGPL - original licence link has changed is not relivant.
38123  *
38124  * Fork - LGPL
38125  * <script type="text/javascript">
38126  */
38127  
38128 /**
38129  * @class Roo.menu.MenuMgr
38130  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38131  * @singleton
38132  */
38133 Roo.menu.MenuMgr = function(){
38134    var menus, active, groups = {}, attached = false, lastShow = new Date();
38135
38136    // private - called when first menu is created
38137    function init(){
38138        menus = {};
38139        active = new Roo.util.MixedCollection();
38140        Roo.get(document).addKeyListener(27, function(){
38141            if(active.length > 0){
38142                hideAll();
38143            }
38144        });
38145    }
38146
38147    // private
38148    function hideAll(){
38149        if(active && active.length > 0){
38150            var c = active.clone();
38151            c.each(function(m){
38152                m.hide();
38153            });
38154        }
38155    }
38156
38157    // private
38158    function onHide(m){
38159        active.remove(m);
38160        if(active.length < 1){
38161            Roo.get(document).un("mousedown", onMouseDown);
38162            attached = false;
38163        }
38164    }
38165
38166    // private
38167    function onShow(m){
38168        var last = active.last();
38169        lastShow = new Date();
38170        active.add(m);
38171        if(!attached){
38172            Roo.get(document).on("mousedown", onMouseDown);
38173            attached = true;
38174        }
38175        if(m.parentMenu){
38176           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38177           m.parentMenu.activeChild = m;
38178        }else if(last && last.isVisible()){
38179           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38180        }
38181    }
38182
38183    // private
38184    function onBeforeHide(m){
38185        if(m.activeChild){
38186            m.activeChild.hide();
38187        }
38188        if(m.autoHideTimer){
38189            clearTimeout(m.autoHideTimer);
38190            delete m.autoHideTimer;
38191        }
38192    }
38193
38194    // private
38195    function onBeforeShow(m){
38196        var pm = m.parentMenu;
38197        if(!pm && !m.allowOtherMenus){
38198            hideAll();
38199        }else if(pm && pm.activeChild && active != m){
38200            pm.activeChild.hide();
38201        }
38202    }
38203
38204    // private
38205    function onMouseDown(e){
38206        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38207            hideAll();
38208        }
38209    }
38210
38211    // private
38212    function onBeforeCheck(mi, state){
38213        if(state){
38214            var g = groups[mi.group];
38215            for(var i = 0, l = g.length; i < l; i++){
38216                if(g[i] != mi){
38217                    g[i].setChecked(false);
38218                }
38219            }
38220        }
38221    }
38222
38223    return {
38224
38225        /**
38226         * Hides all menus that are currently visible
38227         */
38228        hideAll : function(){
38229             hideAll();  
38230        },
38231
38232        // private
38233        register : function(menu){
38234            if(!menus){
38235                init();
38236            }
38237            menus[menu.id] = menu;
38238            menu.on("beforehide", onBeforeHide);
38239            menu.on("hide", onHide);
38240            menu.on("beforeshow", onBeforeShow);
38241            menu.on("show", onShow);
38242            var g = menu.group;
38243            if(g && menu.events["checkchange"]){
38244                if(!groups[g]){
38245                    groups[g] = [];
38246                }
38247                groups[g].push(menu);
38248                menu.on("checkchange", onCheck);
38249            }
38250        },
38251
38252         /**
38253          * Returns a {@link Roo.menu.Menu} object
38254          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38255          * be used to generate and return a new Menu instance.
38256          */
38257        get : function(menu){
38258            if(typeof menu == "string"){ // menu id
38259                return menus[menu];
38260            }else if(menu.events){  // menu instance
38261                return menu;
38262            }else if(typeof menu.length == 'number'){ // array of menu items?
38263                return new Roo.menu.Menu({items:menu});
38264            }else{ // otherwise, must be a config
38265                return new Roo.menu.Menu(menu);
38266            }
38267        },
38268
38269        // private
38270        unregister : function(menu){
38271            delete menus[menu.id];
38272            menu.un("beforehide", onBeforeHide);
38273            menu.un("hide", onHide);
38274            menu.un("beforeshow", onBeforeShow);
38275            menu.un("show", onShow);
38276            var g = menu.group;
38277            if(g && menu.events["checkchange"]){
38278                groups[g].remove(menu);
38279                menu.un("checkchange", onCheck);
38280            }
38281        },
38282
38283        // private
38284        registerCheckable : function(menuItem){
38285            var g = menuItem.group;
38286            if(g){
38287                if(!groups[g]){
38288                    groups[g] = [];
38289                }
38290                groups[g].push(menuItem);
38291                menuItem.on("beforecheckchange", onBeforeCheck);
38292            }
38293        },
38294
38295        // private
38296        unregisterCheckable : function(menuItem){
38297            var g = menuItem.group;
38298            if(g){
38299                groups[g].remove(menuItem);
38300                menuItem.un("beforecheckchange", onBeforeCheck);
38301            }
38302        }
38303    };
38304 }();/*
38305  * Based on:
38306  * Ext JS Library 1.1.1
38307  * Copyright(c) 2006-2007, Ext JS, LLC.
38308  *
38309  * Originally Released Under LGPL - original licence link has changed is not relivant.
38310  *
38311  * Fork - LGPL
38312  * <script type="text/javascript">
38313  */
38314  
38315
38316 /**
38317  * @class Roo.menu.BaseItem
38318  * @extends Roo.Component
38319  * @abstract
38320  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38321  * management and base configuration options shared by all menu components.
38322  * @constructor
38323  * Creates a new BaseItem
38324  * @param {Object} config Configuration options
38325  */
38326 Roo.menu.BaseItem = function(config){
38327     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38328
38329     this.addEvents({
38330         /**
38331          * @event click
38332          * Fires when this item is clicked
38333          * @param {Roo.menu.BaseItem} this
38334          * @param {Roo.EventObject} e
38335          */
38336         click: true,
38337         /**
38338          * @event activate
38339          * Fires when this item is activated
38340          * @param {Roo.menu.BaseItem} this
38341          */
38342         activate : true,
38343         /**
38344          * @event deactivate
38345          * Fires when this item is deactivated
38346          * @param {Roo.menu.BaseItem} this
38347          */
38348         deactivate : true
38349     });
38350
38351     if(this.handler){
38352         this.on("click", this.handler, this.scope, true);
38353     }
38354 };
38355
38356 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38357     /**
38358      * @cfg {Function} handler
38359      * A function that will handle the click event of this menu item (defaults to undefined)
38360      */
38361     /**
38362      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38363      */
38364     canActivate : false,
38365     
38366      /**
38367      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38368      */
38369     hidden: false,
38370     
38371     /**
38372      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38373      */
38374     activeClass : "x-menu-item-active",
38375     /**
38376      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38377      */
38378     hideOnClick : true,
38379     /**
38380      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38381      */
38382     hideDelay : 100,
38383
38384     // private
38385     ctype: "Roo.menu.BaseItem",
38386
38387     // private
38388     actionMode : "container",
38389
38390     // private
38391     render : function(container, parentMenu){
38392         this.parentMenu = parentMenu;
38393         Roo.menu.BaseItem.superclass.render.call(this, container);
38394         this.container.menuItemId = this.id;
38395     },
38396
38397     // private
38398     onRender : function(container, position){
38399         this.el = Roo.get(this.el);
38400         container.dom.appendChild(this.el.dom);
38401     },
38402
38403     // private
38404     onClick : function(e){
38405         if(!this.disabled && this.fireEvent("click", this, e) !== false
38406                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38407             this.handleClick(e);
38408         }else{
38409             e.stopEvent();
38410         }
38411     },
38412
38413     // private
38414     activate : function(){
38415         if(this.disabled){
38416             return false;
38417         }
38418         var li = this.container;
38419         li.addClass(this.activeClass);
38420         this.region = li.getRegion().adjust(2, 2, -2, -2);
38421         this.fireEvent("activate", this);
38422         return true;
38423     },
38424
38425     // private
38426     deactivate : function(){
38427         this.container.removeClass(this.activeClass);
38428         this.fireEvent("deactivate", this);
38429     },
38430
38431     // private
38432     shouldDeactivate : function(e){
38433         return !this.region || !this.region.contains(e.getPoint());
38434     },
38435
38436     // private
38437     handleClick : function(e){
38438         if(this.hideOnClick){
38439             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38440         }
38441     },
38442
38443     // private
38444     expandMenu : function(autoActivate){
38445         // do nothing
38446     },
38447
38448     // private
38449     hideMenu : function(){
38450         // do nothing
38451     }
38452 });/*
38453  * Based on:
38454  * Ext JS Library 1.1.1
38455  * Copyright(c) 2006-2007, Ext JS, LLC.
38456  *
38457  * Originally Released Under LGPL - original licence link has changed is not relivant.
38458  *
38459  * Fork - LGPL
38460  * <script type="text/javascript">
38461  */
38462  
38463 /**
38464  * @class Roo.menu.Adapter
38465  * @extends Roo.menu.BaseItem
38466  * @abstract
38467  * 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.
38468  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38469  * @constructor
38470  * Creates a new Adapter
38471  * @param {Object} config Configuration options
38472  */
38473 Roo.menu.Adapter = function(component, config){
38474     Roo.menu.Adapter.superclass.constructor.call(this, config);
38475     this.component = component;
38476 };
38477 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38478     // private
38479     canActivate : true,
38480
38481     // private
38482     onRender : function(container, position){
38483         this.component.render(container);
38484         this.el = this.component.getEl();
38485     },
38486
38487     // private
38488     activate : function(){
38489         if(this.disabled){
38490             return false;
38491         }
38492         this.component.focus();
38493         this.fireEvent("activate", this);
38494         return true;
38495     },
38496
38497     // private
38498     deactivate : function(){
38499         this.fireEvent("deactivate", this);
38500     },
38501
38502     // private
38503     disable : function(){
38504         this.component.disable();
38505         Roo.menu.Adapter.superclass.disable.call(this);
38506     },
38507
38508     // private
38509     enable : function(){
38510         this.component.enable();
38511         Roo.menu.Adapter.superclass.enable.call(this);
38512     }
38513 });/*
38514  * Based on:
38515  * Ext JS Library 1.1.1
38516  * Copyright(c) 2006-2007, Ext JS, LLC.
38517  *
38518  * Originally Released Under LGPL - original licence link has changed is not relivant.
38519  *
38520  * Fork - LGPL
38521  * <script type="text/javascript">
38522  */
38523
38524 /**
38525  * @class Roo.menu.TextItem
38526  * @extends Roo.menu.BaseItem
38527  * Adds a static text string to a menu, usually used as either a heading or group separator.
38528  * Note: old style constructor with text is still supported.
38529  * 
38530  * @constructor
38531  * Creates a new TextItem
38532  * @param {Object} cfg Configuration
38533  */
38534 Roo.menu.TextItem = function(cfg){
38535     if (typeof(cfg) == 'string') {
38536         this.text = cfg;
38537     } else {
38538         Roo.apply(this,cfg);
38539     }
38540     
38541     Roo.menu.TextItem.superclass.constructor.call(this);
38542 };
38543
38544 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38545     /**
38546      * @cfg {String} text Text to show on item.
38547      */
38548     text : '',
38549     
38550     /**
38551      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38552      */
38553     hideOnClick : false,
38554     /**
38555      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38556      */
38557     itemCls : "x-menu-text",
38558
38559     // private
38560     onRender : function(){
38561         var s = document.createElement("span");
38562         s.className = this.itemCls;
38563         s.innerHTML = this.text;
38564         this.el = s;
38565         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38566     }
38567 });/*
38568  * Based on:
38569  * Ext JS Library 1.1.1
38570  * Copyright(c) 2006-2007, Ext JS, LLC.
38571  *
38572  * Originally Released Under LGPL - original licence link has changed is not relivant.
38573  *
38574  * Fork - LGPL
38575  * <script type="text/javascript">
38576  */
38577
38578 /**
38579  * @class Roo.menu.Separator
38580  * @extends Roo.menu.BaseItem
38581  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38582  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38583  * @constructor
38584  * @param {Object} config Configuration options
38585  */
38586 Roo.menu.Separator = function(config){
38587     Roo.menu.Separator.superclass.constructor.call(this, config);
38588 };
38589
38590 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38591     /**
38592      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38593      */
38594     itemCls : "x-menu-sep",
38595     /**
38596      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38597      */
38598     hideOnClick : false,
38599
38600     // private
38601     onRender : function(li){
38602         var s = document.createElement("span");
38603         s.className = this.itemCls;
38604         s.innerHTML = "&#160;";
38605         this.el = s;
38606         li.addClass("x-menu-sep-li");
38607         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38608     }
38609 });/*
38610  * Based on:
38611  * Ext JS Library 1.1.1
38612  * Copyright(c) 2006-2007, Ext JS, LLC.
38613  *
38614  * Originally Released Under LGPL - original licence link has changed is not relivant.
38615  *
38616  * Fork - LGPL
38617  * <script type="text/javascript">
38618  */
38619 /**
38620  * @class Roo.menu.Item
38621  * @extends Roo.menu.BaseItem
38622  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38623  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38624  * activation and click handling.
38625  * @constructor
38626  * Creates a new Item
38627  * @param {Object} config Configuration options
38628  */
38629 Roo.menu.Item = function(config){
38630     Roo.menu.Item.superclass.constructor.call(this, config);
38631     if(this.menu){
38632         this.menu = Roo.menu.MenuMgr.get(this.menu);
38633     }
38634 };
38635 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38636     /**
38637      * @cfg {Roo.menu.Menu} menu
38638      * A Sub menu
38639      */
38640     /**
38641      * @cfg {String} text
38642      * The text to show on the menu item.
38643      */
38644     text: '',
38645      /**
38646      * @cfg {String} HTML to render in menu
38647      * The text to show on the menu item (HTML version).
38648      */
38649     html: '',
38650     /**
38651      * @cfg {String} icon
38652      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38653      */
38654     icon: undefined,
38655     /**
38656      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38657      */
38658     itemCls : "x-menu-item",
38659     /**
38660      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38661      */
38662     canActivate : true,
38663     /**
38664      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38665      */
38666     showDelay: 200,
38667     // doc'd in BaseItem
38668     hideDelay: 200,
38669
38670     // private
38671     ctype: "Roo.menu.Item",
38672     
38673     // private
38674     onRender : function(container, position){
38675         var el = document.createElement("a");
38676         el.hideFocus = true;
38677         el.unselectable = "on";
38678         el.href = this.href || "#";
38679         if(this.hrefTarget){
38680             el.target = this.hrefTarget;
38681         }
38682         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38683         
38684         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38685         
38686         el.innerHTML = String.format(
38687                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38688                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38689         this.el = el;
38690         Roo.menu.Item.superclass.onRender.call(this, container, position);
38691     },
38692
38693     /**
38694      * Sets the text to display in this menu item
38695      * @param {String} text The text to display
38696      * @param {Boolean} isHTML true to indicate text is pure html.
38697      */
38698     setText : function(text, isHTML){
38699         if (isHTML) {
38700             this.html = text;
38701         } else {
38702             this.text = text;
38703             this.html = '';
38704         }
38705         if(this.rendered){
38706             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38707      
38708             this.el.update(String.format(
38709                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38710                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38711             this.parentMenu.autoWidth();
38712         }
38713     },
38714
38715     // private
38716     handleClick : function(e){
38717         if(!this.href){ // if no link defined, stop the event automatically
38718             e.stopEvent();
38719         }
38720         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38721     },
38722
38723     // private
38724     activate : function(autoExpand){
38725         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38726             this.focus();
38727             if(autoExpand){
38728                 this.expandMenu();
38729             }
38730         }
38731         return true;
38732     },
38733
38734     // private
38735     shouldDeactivate : function(e){
38736         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38737             if(this.menu && this.menu.isVisible()){
38738                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38739             }
38740             return true;
38741         }
38742         return false;
38743     },
38744
38745     // private
38746     deactivate : function(){
38747         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38748         this.hideMenu();
38749     },
38750
38751     // private
38752     expandMenu : function(autoActivate){
38753         if(!this.disabled && this.menu){
38754             clearTimeout(this.hideTimer);
38755             delete this.hideTimer;
38756             if(!this.menu.isVisible() && !this.showTimer){
38757                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38758             }else if (this.menu.isVisible() && autoActivate){
38759                 this.menu.tryActivate(0, 1);
38760             }
38761         }
38762     },
38763
38764     // private
38765     deferExpand : function(autoActivate){
38766         delete this.showTimer;
38767         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38768         if(autoActivate){
38769             this.menu.tryActivate(0, 1);
38770         }
38771     },
38772
38773     // private
38774     hideMenu : function(){
38775         clearTimeout(this.showTimer);
38776         delete this.showTimer;
38777         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38778             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38779         }
38780     },
38781
38782     // private
38783     deferHide : function(){
38784         delete this.hideTimer;
38785         this.menu.hide();
38786     }
38787 });/*
38788  * Based on:
38789  * Ext JS Library 1.1.1
38790  * Copyright(c) 2006-2007, Ext JS, LLC.
38791  *
38792  * Originally Released Under LGPL - original licence link has changed is not relivant.
38793  *
38794  * Fork - LGPL
38795  * <script type="text/javascript">
38796  */
38797  
38798 /**
38799  * @class Roo.menu.CheckItem
38800  * @extends Roo.menu.Item
38801  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38802  * @constructor
38803  * Creates a new CheckItem
38804  * @param {Object} config Configuration options
38805  */
38806 Roo.menu.CheckItem = function(config){
38807     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38808     this.addEvents({
38809         /**
38810          * @event beforecheckchange
38811          * Fires before the checked value is set, providing an opportunity to cancel if needed
38812          * @param {Roo.menu.CheckItem} this
38813          * @param {Boolean} checked The new checked value that will be set
38814          */
38815         "beforecheckchange" : true,
38816         /**
38817          * @event checkchange
38818          * Fires after the checked value has been set
38819          * @param {Roo.menu.CheckItem} this
38820          * @param {Boolean} checked The checked value that was set
38821          */
38822         "checkchange" : true
38823     });
38824     if(this.checkHandler){
38825         this.on('checkchange', this.checkHandler, this.scope);
38826     }
38827 };
38828 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38829     /**
38830      * @cfg {String} group
38831      * All check items with the same group name will automatically be grouped into a single-select
38832      * radio button group (defaults to '')
38833      */
38834     /**
38835      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38836      */
38837     itemCls : "x-menu-item x-menu-check-item",
38838     /**
38839      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38840      */
38841     groupClass : "x-menu-group-item",
38842
38843     /**
38844      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38845      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38846      * initialized with checked = true will be rendered as checked.
38847      */
38848     checked: false,
38849
38850     // private
38851     ctype: "Roo.menu.CheckItem",
38852
38853     // private
38854     onRender : function(c){
38855         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38856         if(this.group){
38857             this.el.addClass(this.groupClass);
38858         }
38859         Roo.menu.MenuMgr.registerCheckable(this);
38860         if(this.checked){
38861             this.checked = false;
38862             this.setChecked(true, true);
38863         }
38864     },
38865
38866     // private
38867     destroy : function(){
38868         if(this.rendered){
38869             Roo.menu.MenuMgr.unregisterCheckable(this);
38870         }
38871         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38872     },
38873
38874     /**
38875      * Set the checked state of this item
38876      * @param {Boolean} checked The new checked value
38877      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38878      */
38879     setChecked : function(state, suppressEvent){
38880         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38881             if(this.container){
38882                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38883             }
38884             this.checked = state;
38885             if(suppressEvent !== true){
38886                 this.fireEvent("checkchange", this, state);
38887             }
38888         }
38889     },
38890
38891     // private
38892     handleClick : function(e){
38893        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38894            this.setChecked(!this.checked);
38895        }
38896        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38897     }
38898 });/*
38899  * Based on:
38900  * Ext JS Library 1.1.1
38901  * Copyright(c) 2006-2007, Ext JS, LLC.
38902  *
38903  * Originally Released Under LGPL - original licence link has changed is not relivant.
38904  *
38905  * Fork - LGPL
38906  * <script type="text/javascript">
38907  */
38908  
38909 /**
38910  * @class Roo.menu.DateItem
38911  * @extends Roo.menu.Adapter
38912  * A menu item that wraps the {@link Roo.DatPicker} component.
38913  * @constructor
38914  * Creates a new DateItem
38915  * @param {Object} config Configuration options
38916  */
38917 Roo.menu.DateItem = function(config){
38918     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38919     /** The Roo.DatePicker object @type Roo.DatePicker */
38920     this.picker = this.component;
38921     this.addEvents({select: true});
38922     
38923     this.picker.on("render", function(picker){
38924         picker.getEl().swallowEvent("click");
38925         picker.container.addClass("x-menu-date-item");
38926     });
38927
38928     this.picker.on("select", this.onSelect, this);
38929 };
38930
38931 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38932     // private
38933     onSelect : function(picker, date){
38934         this.fireEvent("select", this, date, picker);
38935         Roo.menu.DateItem.superclass.handleClick.call(this);
38936     }
38937 });/*
38938  * Based on:
38939  * Ext JS Library 1.1.1
38940  * Copyright(c) 2006-2007, Ext JS, LLC.
38941  *
38942  * Originally Released Under LGPL - original licence link has changed is not relivant.
38943  *
38944  * Fork - LGPL
38945  * <script type="text/javascript">
38946  */
38947  
38948 /**
38949  * @class Roo.menu.ColorItem
38950  * @extends Roo.menu.Adapter
38951  * A menu item that wraps the {@link Roo.ColorPalette} component.
38952  * @constructor
38953  * Creates a new ColorItem
38954  * @param {Object} config Configuration options
38955  */
38956 Roo.menu.ColorItem = function(config){
38957     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38958     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38959     this.palette = this.component;
38960     this.relayEvents(this.palette, ["select"]);
38961     if(this.selectHandler){
38962         this.on('select', this.selectHandler, this.scope);
38963     }
38964 };
38965 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38966  * Based on:
38967  * Ext JS Library 1.1.1
38968  * Copyright(c) 2006-2007, Ext JS, LLC.
38969  *
38970  * Originally Released Under LGPL - original licence link has changed is not relivant.
38971  *
38972  * Fork - LGPL
38973  * <script type="text/javascript">
38974  */
38975  
38976
38977 /**
38978  * @class Roo.menu.DateMenu
38979  * @extends Roo.menu.Menu
38980  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38981  * @constructor
38982  * Creates a new DateMenu
38983  * @param {Object} config Configuration options
38984  */
38985 Roo.menu.DateMenu = function(config){
38986     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38987     this.plain = true;
38988     var di = new Roo.menu.DateItem(config);
38989     this.add(di);
38990     /**
38991      * The {@link Roo.DatePicker} instance for this DateMenu
38992      * @type DatePicker
38993      */
38994     this.picker = di.picker;
38995     /**
38996      * @event select
38997      * @param {DatePicker} picker
38998      * @param {Date} date
38999      */
39000     this.relayEvents(di, ["select"]);
39001     this.on('beforeshow', function(){
39002         if(this.picker){
39003             this.picker.hideMonthPicker(false);
39004         }
39005     }, this);
39006 };
39007 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39008     cls:'x-date-menu'
39009 });/*
39010  * Based on:
39011  * Ext JS Library 1.1.1
39012  * Copyright(c) 2006-2007, Ext JS, LLC.
39013  *
39014  * Originally Released Under LGPL - original licence link has changed is not relivant.
39015  *
39016  * Fork - LGPL
39017  * <script type="text/javascript">
39018  */
39019  
39020
39021 /**
39022  * @class Roo.menu.ColorMenu
39023  * @extends Roo.menu.Menu
39024  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39025  * @constructor
39026  * Creates a new ColorMenu
39027  * @param {Object} config Configuration options
39028  */
39029 Roo.menu.ColorMenu = function(config){
39030     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39031     this.plain = true;
39032     var ci = new Roo.menu.ColorItem(config);
39033     this.add(ci);
39034     /**
39035      * The {@link Roo.ColorPalette} instance for this ColorMenu
39036      * @type ColorPalette
39037      */
39038     this.palette = ci.palette;
39039     /**
39040      * @event select
39041      * @param {ColorPalette} palette
39042      * @param {String} color
39043      */
39044     this.relayEvents(ci, ["select"]);
39045 };
39046 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39047  * Based on:
39048  * Ext JS Library 1.1.1
39049  * Copyright(c) 2006-2007, Ext JS, LLC.
39050  *
39051  * Originally Released Under LGPL - original licence link has changed is not relivant.
39052  *
39053  * Fork - LGPL
39054  * <script type="text/javascript">
39055  */
39056  
39057 /**
39058  * @class Roo.form.TextItem
39059  * @extends Roo.BoxComponent
39060  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39061  * @constructor
39062  * Creates a new TextItem
39063  * @param {Object} config Configuration options
39064  */
39065 Roo.form.TextItem = function(config){
39066     Roo.form.TextItem.superclass.constructor.call(this, config);
39067 };
39068
39069 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39070     
39071     /**
39072      * @cfg {String} tag the tag for this item (default div)
39073      */
39074     tag : 'div',
39075     /**
39076      * @cfg {String} html the content for this item
39077      */
39078     html : '',
39079     
39080     getAutoCreate : function()
39081     {
39082         var cfg = {
39083             id: this.id,
39084             tag: this.tag,
39085             html: this.html,
39086             cls: 'x-form-item'
39087         };
39088         
39089         return cfg;
39090         
39091     },
39092     
39093     onRender : function(ct, position)
39094     {
39095         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39096         
39097         if(!this.el){
39098             var cfg = this.getAutoCreate();
39099             if(!cfg.name){
39100                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39101             }
39102             if (!cfg.name.length) {
39103                 delete cfg.name;
39104             }
39105             this.el = ct.createChild(cfg, position);
39106         }
39107     },
39108     /*
39109      * setHTML
39110      * @param {String} html update the Contents of the element.
39111      */
39112     setHTML : function(html)
39113     {
39114         this.fieldEl.dom.innerHTML = html;
39115     }
39116     
39117 });/*
39118  * Based on:
39119  * Ext JS Library 1.1.1
39120  * Copyright(c) 2006-2007, Ext JS, LLC.
39121  *
39122  * Originally Released Under LGPL - original licence link has changed is not relivant.
39123  *
39124  * Fork - LGPL
39125  * <script type="text/javascript">
39126  */
39127  
39128 /**
39129  * @class Roo.form.Field
39130  * @extends Roo.BoxComponent
39131  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39132  * @constructor
39133  * Creates a new Field
39134  * @param {Object} config Configuration options
39135  */
39136 Roo.form.Field = function(config){
39137     Roo.form.Field.superclass.constructor.call(this, config);
39138 };
39139
39140 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39141     /**
39142      * @cfg {String} fieldLabel Label to use when rendering a form.
39143      */
39144        /**
39145      * @cfg {String} qtip Mouse over tip
39146      */
39147      
39148     /**
39149      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39150      */
39151     invalidClass : "x-form-invalid",
39152     /**
39153      * @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")
39154      */
39155     invalidText : "The value in this field is invalid",
39156     /**
39157      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39158      */
39159     focusClass : "x-form-focus",
39160     /**
39161      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39162       automatic validation (defaults to "keyup").
39163      */
39164     validationEvent : "keyup",
39165     /**
39166      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39167      */
39168     validateOnBlur : true,
39169     /**
39170      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39171      */
39172     validationDelay : 250,
39173     /**
39174      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39175      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39176      */
39177     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39178     /**
39179      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39180      */
39181     fieldClass : "x-form-field",
39182     /**
39183      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39184      *<pre>
39185 Value         Description
39186 -----------   ----------------------------------------------------------------------
39187 qtip          Display a quick tip when the user hovers over the field
39188 title         Display a default browser title attribute popup
39189 under         Add a block div beneath the field containing the error text
39190 side          Add an error icon to the right of the field with a popup on hover
39191 [element id]  Add the error text directly to the innerHTML of the specified element
39192 </pre>
39193      */
39194     msgTarget : 'qtip',
39195     /**
39196      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39197      */
39198     msgFx : 'normal',
39199
39200     /**
39201      * @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.
39202      */
39203     readOnly : false,
39204
39205     /**
39206      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39207      */
39208     disabled : false,
39209
39210     /**
39211      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39212      */
39213     inputType : undefined,
39214     
39215     /**
39216      * @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).
39217          */
39218         tabIndex : undefined,
39219         
39220     // private
39221     isFormField : true,
39222
39223     // private
39224     hasFocus : false,
39225     /**
39226      * @property {Roo.Element} fieldEl
39227      * Element Containing the rendered Field (with label etc.)
39228      */
39229     /**
39230      * @cfg {Mixed} value A value to initialize this field with.
39231      */
39232     value : undefined,
39233
39234     /**
39235      * @cfg {String} name The field's HTML name attribute.
39236      */
39237     /**
39238      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39239      */
39240     // private
39241     loadedValue : false,
39242      
39243      
39244         // private ??
39245         initComponent : function(){
39246         Roo.form.Field.superclass.initComponent.call(this);
39247         this.addEvents({
39248             /**
39249              * @event focus
39250              * Fires when this field receives input focus.
39251              * @param {Roo.form.Field} this
39252              */
39253             focus : true,
39254             /**
39255              * @event blur
39256              * Fires when this field loses input focus.
39257              * @param {Roo.form.Field} this
39258              */
39259             blur : true,
39260             /**
39261              * @event specialkey
39262              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39263              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39264              * @param {Roo.form.Field} this
39265              * @param {Roo.EventObject} e The event object
39266              */
39267             specialkey : true,
39268             /**
39269              * @event change
39270              * Fires just before the field blurs if the field value has changed.
39271              * @param {Roo.form.Field} this
39272              * @param {Mixed} newValue The new value
39273              * @param {Mixed} oldValue The original value
39274              */
39275             change : true,
39276             /**
39277              * @event invalid
39278              * Fires after the field has been marked as invalid.
39279              * @param {Roo.form.Field} this
39280              * @param {String} msg The validation message
39281              */
39282             invalid : true,
39283             /**
39284              * @event valid
39285              * Fires after the field has been validated with no errors.
39286              * @param {Roo.form.Field} this
39287              */
39288             valid : true,
39289              /**
39290              * @event keyup
39291              * Fires after the key up
39292              * @param {Roo.form.Field} this
39293              * @param {Roo.EventObject}  e The event Object
39294              */
39295             keyup : true
39296         });
39297     },
39298
39299     /**
39300      * Returns the name attribute of the field if available
39301      * @return {String} name The field name
39302      */
39303     getName: function(){
39304          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39305     },
39306
39307     // private
39308     onRender : function(ct, position){
39309         Roo.form.Field.superclass.onRender.call(this, ct, position);
39310         if(!this.el){
39311             var cfg = this.getAutoCreate();
39312             if(!cfg.name){
39313                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39314             }
39315             if (!cfg.name.length) {
39316                 delete cfg.name;
39317             }
39318             if(this.inputType){
39319                 cfg.type = this.inputType;
39320             }
39321             this.el = ct.createChild(cfg, position);
39322         }
39323         var type = this.el.dom.type;
39324         if(type){
39325             if(type == 'password'){
39326                 type = 'text';
39327             }
39328             this.el.addClass('x-form-'+type);
39329         }
39330         if(this.readOnly){
39331             this.el.dom.readOnly = true;
39332         }
39333         if(this.tabIndex !== undefined){
39334             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39335         }
39336
39337         this.el.addClass([this.fieldClass, this.cls]);
39338         this.initValue();
39339     },
39340
39341     /**
39342      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39343      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39344      * @return {Roo.form.Field} this
39345      */
39346     applyTo : function(target){
39347         this.allowDomMove = false;
39348         this.el = Roo.get(target);
39349         this.render(this.el.dom.parentNode);
39350         return this;
39351     },
39352
39353     // private
39354     initValue : function(){
39355         if(this.value !== undefined){
39356             this.setValue(this.value);
39357         }else if(this.el.dom.value.length > 0){
39358             this.setValue(this.el.dom.value);
39359         }
39360     },
39361
39362     /**
39363      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39364      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39365      */
39366     isDirty : function() {
39367         if(this.disabled) {
39368             return false;
39369         }
39370         return String(this.getValue()) !== String(this.originalValue);
39371     },
39372
39373     /**
39374      * stores the current value in loadedValue
39375      */
39376     resetHasChanged : function()
39377     {
39378         this.loadedValue = String(this.getValue());
39379     },
39380     /**
39381      * checks the current value against the 'loaded' value.
39382      * Note - will return false if 'resetHasChanged' has not been called first.
39383      */
39384     hasChanged : function()
39385     {
39386         if(this.disabled || this.readOnly) {
39387             return false;
39388         }
39389         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39390     },
39391     
39392     
39393     
39394     // private
39395     afterRender : function(){
39396         Roo.form.Field.superclass.afterRender.call(this);
39397         this.initEvents();
39398     },
39399
39400     // private
39401     fireKey : function(e){
39402         //Roo.log('field ' + e.getKey());
39403         if(e.isNavKeyPress()){
39404             this.fireEvent("specialkey", this, e);
39405         }
39406     },
39407
39408     /**
39409      * Resets the current field value to the originally loaded value and clears any validation messages
39410      */
39411     reset : function(){
39412         this.setValue(this.resetValue);
39413         this.originalValue = this.getValue();
39414         this.clearInvalid();
39415     },
39416
39417     // private
39418     initEvents : function(){
39419         // safari killled keypress - so keydown is now used..
39420         this.el.on("keydown" , this.fireKey,  this);
39421         this.el.on("focus", this.onFocus,  this);
39422         this.el.on("blur", this.onBlur,  this);
39423         this.el.relayEvent('keyup', this);
39424
39425         // reference to original value for reset
39426         this.originalValue = this.getValue();
39427         this.resetValue =  this.getValue();
39428     },
39429
39430     // private
39431     onFocus : function(){
39432         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39433             this.el.addClass(this.focusClass);
39434         }
39435         if(!this.hasFocus){
39436             this.hasFocus = true;
39437             this.startValue = this.getValue();
39438             this.fireEvent("focus", this);
39439         }
39440     },
39441
39442     beforeBlur : Roo.emptyFn,
39443
39444     // private
39445     onBlur : function(){
39446         this.beforeBlur();
39447         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39448             this.el.removeClass(this.focusClass);
39449         }
39450         this.hasFocus = false;
39451         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39452             this.validate();
39453         }
39454         var v = this.getValue();
39455         if(String(v) !== String(this.startValue)){
39456             this.fireEvent('change', this, v, this.startValue);
39457         }
39458         this.fireEvent("blur", this);
39459     },
39460
39461     /**
39462      * Returns whether or not the field value is currently valid
39463      * @param {Boolean} preventMark True to disable marking the field invalid
39464      * @return {Boolean} True if the value is valid, else false
39465      */
39466     isValid : function(preventMark){
39467         if(this.disabled){
39468             return true;
39469         }
39470         var restore = this.preventMark;
39471         this.preventMark = preventMark === true;
39472         var v = this.validateValue(this.processValue(this.getRawValue()));
39473         this.preventMark = restore;
39474         return v;
39475     },
39476
39477     /**
39478      * Validates the field value
39479      * @return {Boolean} True if the value is valid, else false
39480      */
39481     validate : function(){
39482         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39483             this.clearInvalid();
39484             return true;
39485         }
39486         return false;
39487     },
39488
39489     processValue : function(value){
39490         return value;
39491     },
39492
39493     // private
39494     // Subclasses should provide the validation implementation by overriding this
39495     validateValue : function(value){
39496         return true;
39497     },
39498
39499     /**
39500      * Mark this field as invalid
39501      * @param {String} msg The validation message
39502      */
39503     markInvalid : function(msg){
39504         if(!this.rendered || this.preventMark){ // not rendered
39505             return;
39506         }
39507         
39508         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39509         
39510         obj.el.addClass(this.invalidClass);
39511         msg = msg || this.invalidText;
39512         switch(this.msgTarget){
39513             case 'qtip':
39514                 obj.el.dom.qtip = msg;
39515                 obj.el.dom.qclass = 'x-form-invalid-tip';
39516                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39517                     Roo.QuickTips.enable();
39518                 }
39519                 break;
39520             case 'title':
39521                 this.el.dom.title = msg;
39522                 break;
39523             case 'under':
39524                 if(!this.errorEl){
39525                     var elp = this.el.findParent('.x-form-element', 5, true);
39526                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39527                     this.errorEl.setWidth(elp.getWidth(true)-20);
39528                 }
39529                 this.errorEl.update(msg);
39530                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39531                 break;
39532             case 'side':
39533                 if(!this.errorIcon){
39534                     var elp = this.el.findParent('.x-form-element', 5, true);
39535                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39536                 }
39537                 this.alignErrorIcon();
39538                 this.errorIcon.dom.qtip = msg;
39539                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39540                 this.errorIcon.show();
39541                 this.on('resize', this.alignErrorIcon, this);
39542                 break;
39543             default:
39544                 var t = Roo.getDom(this.msgTarget);
39545                 t.innerHTML = msg;
39546                 t.style.display = this.msgDisplay;
39547                 break;
39548         }
39549         this.fireEvent('invalid', this, msg);
39550     },
39551
39552     // private
39553     alignErrorIcon : function(){
39554         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39555     },
39556
39557     /**
39558      * Clear any invalid styles/messages for this field
39559      */
39560     clearInvalid : function(){
39561         if(!this.rendered || this.preventMark){ // not rendered
39562             return;
39563         }
39564         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39565         
39566         obj.el.removeClass(this.invalidClass);
39567         switch(this.msgTarget){
39568             case 'qtip':
39569                 obj.el.dom.qtip = '';
39570                 break;
39571             case 'title':
39572                 this.el.dom.title = '';
39573                 break;
39574             case 'under':
39575                 if(this.errorEl){
39576                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39577                 }
39578                 break;
39579             case 'side':
39580                 if(this.errorIcon){
39581                     this.errorIcon.dom.qtip = '';
39582                     this.errorIcon.hide();
39583                     this.un('resize', this.alignErrorIcon, this);
39584                 }
39585                 break;
39586             default:
39587                 var t = Roo.getDom(this.msgTarget);
39588                 t.innerHTML = '';
39589                 t.style.display = 'none';
39590                 break;
39591         }
39592         this.fireEvent('valid', this);
39593     },
39594
39595     /**
39596      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39597      * @return {Mixed} value The field value
39598      */
39599     getRawValue : function(){
39600         var v = this.el.getValue();
39601         
39602         return v;
39603     },
39604
39605     /**
39606      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39607      * @return {Mixed} value The field value
39608      */
39609     getValue : function(){
39610         var v = this.el.getValue();
39611          
39612         return v;
39613     },
39614
39615     /**
39616      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39617      * @param {Mixed} value The value to set
39618      */
39619     setRawValue : function(v){
39620         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39621     },
39622
39623     /**
39624      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39625      * @param {Mixed} value The value to set
39626      */
39627     setValue : function(v){
39628         this.value = v;
39629         if(this.rendered){
39630             this.el.dom.value = (v === null || v === undefined ? '' : v);
39631              this.validate();
39632         }
39633     },
39634
39635     adjustSize : function(w, h){
39636         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39637         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39638         return s;
39639     },
39640
39641     adjustWidth : function(tag, w){
39642         tag = tag.toLowerCase();
39643         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39644             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39645                 if(tag == 'input'){
39646                     return w + 2;
39647                 }
39648                 if(tag == 'textarea'){
39649                     return w-2;
39650                 }
39651             }else if(Roo.isOpera){
39652                 if(tag == 'input'){
39653                     return w + 2;
39654                 }
39655                 if(tag == 'textarea'){
39656                     return w-2;
39657                 }
39658             }
39659         }
39660         return w;
39661     }
39662 });
39663
39664
39665 // anything other than normal should be considered experimental
39666 Roo.form.Field.msgFx = {
39667     normal : {
39668         show: function(msgEl, f){
39669             msgEl.setDisplayed('block');
39670         },
39671
39672         hide : function(msgEl, f){
39673             msgEl.setDisplayed(false).update('');
39674         }
39675     },
39676
39677     slide : {
39678         show: function(msgEl, f){
39679             msgEl.slideIn('t', {stopFx:true});
39680         },
39681
39682         hide : function(msgEl, f){
39683             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39684         }
39685     },
39686
39687     slideRight : {
39688         show: function(msgEl, f){
39689             msgEl.fixDisplay();
39690             msgEl.alignTo(f.el, 'tl-tr');
39691             msgEl.slideIn('l', {stopFx:true});
39692         },
39693
39694         hide : function(msgEl, f){
39695             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39696         }
39697     }
39698 };/*
39699  * Based on:
39700  * Ext JS Library 1.1.1
39701  * Copyright(c) 2006-2007, Ext JS, LLC.
39702  *
39703  * Originally Released Under LGPL - original licence link has changed is not relivant.
39704  *
39705  * Fork - LGPL
39706  * <script type="text/javascript">
39707  */
39708  
39709
39710 /**
39711  * @class Roo.form.TextField
39712  * @extends Roo.form.Field
39713  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39714  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39715  * @constructor
39716  * Creates a new TextField
39717  * @param {Object} config Configuration options
39718  */
39719 Roo.form.TextField = function(config){
39720     Roo.form.TextField.superclass.constructor.call(this, config);
39721     this.addEvents({
39722         /**
39723          * @event autosize
39724          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39725          * according to the default logic, but this event provides a hook for the developer to apply additional
39726          * logic at runtime to resize the field if needed.
39727              * @param {Roo.form.Field} this This text field
39728              * @param {Number} width The new field width
39729              */
39730         autosize : true
39731     });
39732 };
39733
39734 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39735     /**
39736      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39737      */
39738     grow : false,
39739     /**
39740      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39741      */
39742     growMin : 30,
39743     /**
39744      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39745      */
39746     growMax : 800,
39747     /**
39748      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39749      */
39750     vtype : null,
39751     /**
39752      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39753      */
39754     maskRe : null,
39755     /**
39756      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39757      */
39758     disableKeyFilter : false,
39759     /**
39760      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39761      */
39762     allowBlank : true,
39763     /**
39764      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39765      */
39766     minLength : 0,
39767     /**
39768      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39769      */
39770     maxLength : Number.MAX_VALUE,
39771     /**
39772      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39773      */
39774     minLengthText : "The minimum length for this field is {0}",
39775     /**
39776      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39777      */
39778     maxLengthText : "The maximum length for this field is {0}",
39779     /**
39780      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39781      */
39782     selectOnFocus : false,
39783     /**
39784      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39785      */    
39786     allowLeadingSpace : false,
39787     /**
39788      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39789      */
39790     blankText : "This field is required",
39791     /**
39792      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39793      * If available, this function will be called only after the basic validators all return true, and will be passed the
39794      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39795      */
39796     validator : null,
39797     /**
39798      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39799      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39800      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39801      */
39802     regex : null,
39803     /**
39804      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39805      */
39806     regexText : "",
39807     /**
39808      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39809      */
39810     emptyText : null,
39811    
39812
39813     // private
39814     initEvents : function()
39815     {
39816         if (this.emptyText) {
39817             this.el.attr('placeholder', this.emptyText);
39818         }
39819         
39820         Roo.form.TextField.superclass.initEvents.call(this);
39821         if(this.validationEvent == 'keyup'){
39822             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39823             this.el.on('keyup', this.filterValidation, this);
39824         }
39825         else if(this.validationEvent !== false){
39826             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39827         }
39828         
39829         if(this.selectOnFocus){
39830             this.on("focus", this.preFocus, this);
39831         }
39832         if (!this.allowLeadingSpace) {
39833             this.on('blur', this.cleanLeadingSpace, this);
39834         }
39835         
39836         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39837             this.el.on("keypress", this.filterKeys, this);
39838         }
39839         if(this.grow){
39840             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39841             this.el.on("click", this.autoSize,  this);
39842         }
39843         if(this.el.is('input[type=password]') && Roo.isSafari){
39844             this.el.on('keydown', this.SafariOnKeyDown, this);
39845         }
39846     },
39847
39848     processValue : function(value){
39849         if(this.stripCharsRe){
39850             var newValue = value.replace(this.stripCharsRe, '');
39851             if(newValue !== value){
39852                 this.setRawValue(newValue);
39853                 return newValue;
39854             }
39855         }
39856         return value;
39857     },
39858
39859     filterValidation : function(e){
39860         if(!e.isNavKeyPress()){
39861             this.validationTask.delay(this.validationDelay);
39862         }
39863     },
39864
39865     // private
39866     onKeyUp : function(e){
39867         if(!e.isNavKeyPress()){
39868             this.autoSize();
39869         }
39870     },
39871     // private - clean the leading white space
39872     cleanLeadingSpace : function(e)
39873     {
39874         if ( this.inputType == 'file') {
39875             return;
39876         }
39877         
39878         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39879     },
39880     /**
39881      * Resets the current field value to the originally-loaded value and clears any validation messages.
39882      *  
39883      */
39884     reset : function(){
39885         Roo.form.TextField.superclass.reset.call(this);
39886        
39887     }, 
39888     // private
39889     preFocus : function(){
39890         
39891         if(this.selectOnFocus){
39892             this.el.dom.select();
39893         }
39894     },
39895
39896     
39897     // private
39898     filterKeys : function(e){
39899         var k = e.getKey();
39900         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39901             return;
39902         }
39903         var c = e.getCharCode(), cc = String.fromCharCode(c);
39904         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39905             return;
39906         }
39907         if(!this.maskRe.test(cc)){
39908             e.stopEvent();
39909         }
39910     },
39911
39912     setValue : function(v){
39913         
39914         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39915         
39916         this.autoSize();
39917     },
39918
39919     /**
39920      * Validates a value according to the field's validation rules and marks the field as invalid
39921      * if the validation fails
39922      * @param {Mixed} value The value to validate
39923      * @return {Boolean} True if the value is valid, else false
39924      */
39925     validateValue : function(value){
39926         if(value.length < 1)  { // if it's blank
39927              if(this.allowBlank){
39928                 this.clearInvalid();
39929                 return true;
39930              }else{
39931                 this.markInvalid(this.blankText);
39932                 return false;
39933              }
39934         }
39935         if(value.length < this.minLength){
39936             this.markInvalid(String.format(this.minLengthText, this.minLength));
39937             return false;
39938         }
39939         if(value.length > this.maxLength){
39940             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39941             return false;
39942         }
39943         if(this.vtype){
39944             var vt = Roo.form.VTypes;
39945             if(!vt[this.vtype](value, this)){
39946                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39947                 return false;
39948             }
39949         }
39950         if(typeof this.validator == "function"){
39951             var msg = this.validator(value);
39952             if(msg !== true){
39953                 this.markInvalid(msg);
39954                 return false;
39955             }
39956         }
39957         if(this.regex && !this.regex.test(value)){
39958             this.markInvalid(this.regexText);
39959             return false;
39960         }
39961         return true;
39962     },
39963
39964     /**
39965      * Selects text in this field
39966      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39967      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39968      */
39969     selectText : function(start, end){
39970         var v = this.getRawValue();
39971         if(v.length > 0){
39972             start = start === undefined ? 0 : start;
39973             end = end === undefined ? v.length : end;
39974             var d = this.el.dom;
39975             if(d.setSelectionRange){
39976                 d.setSelectionRange(start, end);
39977             }else if(d.createTextRange){
39978                 var range = d.createTextRange();
39979                 range.moveStart("character", start);
39980                 range.moveEnd("character", v.length-end);
39981                 range.select();
39982             }
39983         }
39984     },
39985
39986     /**
39987      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39988      * This only takes effect if grow = true, and fires the autosize event.
39989      */
39990     autoSize : function(){
39991         if(!this.grow || !this.rendered){
39992             return;
39993         }
39994         if(!this.metrics){
39995             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39996         }
39997         var el = this.el;
39998         var v = el.dom.value;
39999         var d = document.createElement('div');
40000         d.appendChild(document.createTextNode(v));
40001         v = d.innerHTML;
40002         d = null;
40003         v += "&#160;";
40004         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40005         this.el.setWidth(w);
40006         this.fireEvent("autosize", this, w);
40007     },
40008     
40009     // private
40010     SafariOnKeyDown : function(event)
40011     {
40012         // this is a workaround for a password hang bug on chrome/ webkit.
40013         
40014         var isSelectAll = false;
40015         
40016         if(this.el.dom.selectionEnd > 0){
40017             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40018         }
40019         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40020             event.preventDefault();
40021             this.setValue('');
40022             return;
40023         }
40024         
40025         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40026             
40027             event.preventDefault();
40028             // this is very hacky as keydown always get's upper case.
40029             
40030             var cc = String.fromCharCode(event.getCharCode());
40031             
40032             
40033             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40034             
40035         }
40036         
40037         
40038     }
40039 });/*
40040  * Based on:
40041  * Ext JS Library 1.1.1
40042  * Copyright(c) 2006-2007, Ext JS, LLC.
40043  *
40044  * Originally Released Under LGPL - original licence link has changed is not relivant.
40045  *
40046  * Fork - LGPL
40047  * <script type="text/javascript">
40048  */
40049  
40050 /**
40051  * @class Roo.form.Hidden
40052  * @extends Roo.form.TextField
40053  * Simple Hidden element used on forms 
40054  * 
40055  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40056  * 
40057  * @constructor
40058  * Creates a new Hidden form element.
40059  * @param {Object} config Configuration options
40060  */
40061
40062
40063
40064 // easy hidden field...
40065 Roo.form.Hidden = function(config){
40066     Roo.form.Hidden.superclass.constructor.call(this, config);
40067 };
40068   
40069 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40070     fieldLabel:      '',
40071     inputType:      'hidden',
40072     width:          50,
40073     allowBlank:     true,
40074     labelSeparator: '',
40075     hidden:         true,
40076     itemCls :       'x-form-item-display-none'
40077
40078
40079 });
40080
40081
40082 /*
40083  * Based on:
40084  * Ext JS Library 1.1.1
40085  * Copyright(c) 2006-2007, Ext JS, LLC.
40086  *
40087  * Originally Released Under LGPL - original licence link has changed is not relivant.
40088  *
40089  * Fork - LGPL
40090  * <script type="text/javascript">
40091  */
40092  
40093 /**
40094  * @class Roo.form.TriggerField
40095  * @extends Roo.form.TextField
40096  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40097  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40098  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40099  * for which you can provide a custom implementation.  For example:
40100  * <pre><code>
40101 var trigger = new Roo.form.TriggerField();
40102 trigger.onTriggerClick = myTriggerFn;
40103 trigger.applyTo('my-field');
40104 </code></pre>
40105  *
40106  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40107  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40108  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40109  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40110  * @constructor
40111  * Create a new TriggerField.
40112  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40113  * to the base TextField)
40114  */
40115 Roo.form.TriggerField = function(config){
40116     this.mimicing = false;
40117     Roo.form.TriggerField.superclass.constructor.call(this, config);
40118 };
40119
40120 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40121     /**
40122      * @cfg {String} triggerClass A CSS class to apply to the trigger
40123      */
40124     /**
40125      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40126      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40127      */
40128     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40129     /**
40130      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40131      */
40132     hideTrigger:false,
40133
40134     /** @cfg {Boolean} grow @hide */
40135     /** @cfg {Number} growMin @hide */
40136     /** @cfg {Number} growMax @hide */
40137
40138     /**
40139      * @hide 
40140      * @method
40141      */
40142     autoSize: Roo.emptyFn,
40143     // private
40144     monitorTab : true,
40145     // private
40146     deferHeight : true,
40147
40148     
40149     actionMode : 'wrap',
40150     // private
40151     onResize : function(w, h){
40152         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40153         if(typeof w == 'number'){
40154             var x = w - this.trigger.getWidth();
40155             this.el.setWidth(this.adjustWidth('input', x));
40156             this.trigger.setStyle('left', x+'px');
40157         }
40158     },
40159
40160     // private
40161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40162
40163     // private
40164     getResizeEl : function(){
40165         return this.wrap;
40166     },
40167
40168     // private
40169     getPositionEl : function(){
40170         return this.wrap;
40171     },
40172
40173     // private
40174     alignErrorIcon : function(){
40175         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40176     },
40177
40178     // private
40179     onRender : function(ct, position){
40180         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40181         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40182         this.trigger = this.wrap.createChild(this.triggerConfig ||
40183                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40184         if(this.hideTrigger){
40185             this.trigger.setDisplayed(false);
40186         }
40187         this.initTrigger();
40188         if(!this.width){
40189             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40190         }
40191     },
40192
40193     // private
40194     initTrigger : function(){
40195         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40196         this.trigger.addClassOnOver('x-form-trigger-over');
40197         this.trigger.addClassOnClick('x-form-trigger-click');
40198     },
40199
40200     // private
40201     onDestroy : function(){
40202         if(this.trigger){
40203             this.trigger.removeAllListeners();
40204             this.trigger.remove();
40205         }
40206         if(this.wrap){
40207             this.wrap.remove();
40208         }
40209         Roo.form.TriggerField.superclass.onDestroy.call(this);
40210     },
40211
40212     // private
40213     onFocus : function(){
40214         Roo.form.TriggerField.superclass.onFocus.call(this);
40215         if(!this.mimicing){
40216             this.wrap.addClass('x-trigger-wrap-focus');
40217             this.mimicing = true;
40218             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40219             if(this.monitorTab){
40220                 this.el.on("keydown", this.checkTab, this);
40221             }
40222         }
40223     },
40224
40225     // private
40226     checkTab : function(e){
40227         if(e.getKey() == e.TAB){
40228             this.triggerBlur();
40229         }
40230     },
40231
40232     // private
40233     onBlur : function(){
40234         // do nothing
40235     },
40236
40237     // private
40238     mimicBlur : function(e, t){
40239         if(!this.wrap.contains(t) && this.validateBlur()){
40240             this.triggerBlur();
40241         }
40242     },
40243
40244     // private
40245     triggerBlur : function(){
40246         this.mimicing = false;
40247         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40248         if(this.monitorTab){
40249             this.el.un("keydown", this.checkTab, this);
40250         }
40251         this.wrap.removeClass('x-trigger-wrap-focus');
40252         Roo.form.TriggerField.superclass.onBlur.call(this);
40253     },
40254
40255     // private
40256     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40257     validateBlur : function(e, t){
40258         return true;
40259     },
40260
40261     // private
40262     onDisable : function(){
40263         Roo.form.TriggerField.superclass.onDisable.call(this);
40264         if(this.wrap){
40265             this.wrap.addClass('x-item-disabled');
40266         }
40267     },
40268
40269     // private
40270     onEnable : function(){
40271         Roo.form.TriggerField.superclass.onEnable.call(this);
40272         if(this.wrap){
40273             this.wrap.removeClass('x-item-disabled');
40274         }
40275     },
40276
40277     // private
40278     onShow : function(){
40279         var ae = this.getActionEl();
40280         
40281         if(ae){
40282             ae.dom.style.display = '';
40283             ae.dom.style.visibility = 'visible';
40284         }
40285     },
40286
40287     // private
40288     
40289     onHide : function(){
40290         var ae = this.getActionEl();
40291         ae.dom.style.display = 'none';
40292     },
40293
40294     /**
40295      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40296      * by an implementing function.
40297      * @method
40298      * @param {EventObject} e
40299      */
40300     onTriggerClick : Roo.emptyFn
40301 });
40302
40303 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40304 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40305 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40306 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40307     initComponent : function(){
40308         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40309
40310         this.triggerConfig = {
40311             tag:'span', cls:'x-form-twin-triggers', cn:[
40312             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40313             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40314         ]};
40315     },
40316
40317     getTrigger : function(index){
40318         return this.triggers[index];
40319     },
40320
40321     initTrigger : function(){
40322         var ts = this.trigger.select('.x-form-trigger', true);
40323         this.wrap.setStyle('overflow', 'hidden');
40324         var triggerField = this;
40325         ts.each(function(t, all, index){
40326             t.hide = function(){
40327                 var w = triggerField.wrap.getWidth();
40328                 this.dom.style.display = 'none';
40329                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40330             };
40331             t.show = function(){
40332                 var w = triggerField.wrap.getWidth();
40333                 this.dom.style.display = '';
40334                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40335             };
40336             var triggerIndex = 'Trigger'+(index+1);
40337
40338             if(this['hide'+triggerIndex]){
40339                 t.dom.style.display = 'none';
40340             }
40341             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40342             t.addClassOnOver('x-form-trigger-over');
40343             t.addClassOnClick('x-form-trigger-click');
40344         }, this);
40345         this.triggers = ts.elements;
40346     },
40347
40348     onTrigger1Click : Roo.emptyFn,
40349     onTrigger2Click : Roo.emptyFn
40350 });/*
40351  * Based on:
40352  * Ext JS Library 1.1.1
40353  * Copyright(c) 2006-2007, Ext JS, LLC.
40354  *
40355  * Originally Released Under LGPL - original licence link has changed is not relivant.
40356  *
40357  * Fork - LGPL
40358  * <script type="text/javascript">
40359  */
40360  
40361 /**
40362  * @class Roo.form.TextArea
40363  * @extends Roo.form.TextField
40364  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40365  * support for auto-sizing.
40366  * @constructor
40367  * Creates a new TextArea
40368  * @param {Object} config Configuration options
40369  */
40370 Roo.form.TextArea = function(config){
40371     Roo.form.TextArea.superclass.constructor.call(this, config);
40372     // these are provided exchanges for backwards compat
40373     // minHeight/maxHeight were replaced by growMin/growMax to be
40374     // compatible with TextField growing config values
40375     if(this.minHeight !== undefined){
40376         this.growMin = this.minHeight;
40377     }
40378     if(this.maxHeight !== undefined){
40379         this.growMax = this.maxHeight;
40380     }
40381 };
40382
40383 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40384     /**
40385      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40386      */
40387     growMin : 60,
40388     /**
40389      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40390      */
40391     growMax: 1000,
40392     /**
40393      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40394      * in the field (equivalent to setting overflow: hidden, defaults to false)
40395      */
40396     preventScrollbars: false,
40397     /**
40398      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40399      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40400      */
40401
40402     // private
40403     onRender : function(ct, position){
40404         if(!this.el){
40405             this.defaultAutoCreate = {
40406                 tag: "textarea",
40407                 style:"width:300px;height:60px;",
40408                 autocomplete: "new-password"
40409             };
40410         }
40411         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40412         if(this.grow){
40413             this.textSizeEl = Roo.DomHelper.append(document.body, {
40414                 tag: "pre", cls: "x-form-grow-sizer"
40415             });
40416             if(this.preventScrollbars){
40417                 this.el.setStyle("overflow", "hidden");
40418             }
40419             this.el.setHeight(this.growMin);
40420         }
40421     },
40422
40423     onDestroy : function(){
40424         if(this.textSizeEl){
40425             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40426         }
40427         Roo.form.TextArea.superclass.onDestroy.call(this);
40428     },
40429
40430     // private
40431     onKeyUp : function(e){
40432         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40433             this.autoSize();
40434         }
40435     },
40436
40437     /**
40438      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40439      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40440      */
40441     autoSize : function(){
40442         if(!this.grow || !this.textSizeEl){
40443             return;
40444         }
40445         var el = this.el;
40446         var v = el.dom.value;
40447         var ts = this.textSizeEl;
40448
40449         ts.innerHTML = '';
40450         ts.appendChild(document.createTextNode(v));
40451         v = ts.innerHTML;
40452
40453         Roo.fly(ts).setWidth(this.el.getWidth());
40454         if(v.length < 1){
40455             v = "&#160;&#160;";
40456         }else{
40457             if(Roo.isIE){
40458                 v = v.replace(/\n/g, '<p>&#160;</p>');
40459             }
40460             v += "&#160;\n&#160;";
40461         }
40462         ts.innerHTML = v;
40463         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40464         if(h != this.lastHeight){
40465             this.lastHeight = h;
40466             this.el.setHeight(h);
40467             this.fireEvent("autosize", this, h);
40468         }
40469     }
40470 });/*
40471  * Based on:
40472  * Ext JS Library 1.1.1
40473  * Copyright(c) 2006-2007, Ext JS, LLC.
40474  *
40475  * Originally Released Under LGPL - original licence link has changed is not relivant.
40476  *
40477  * Fork - LGPL
40478  * <script type="text/javascript">
40479  */
40480  
40481
40482 /**
40483  * @class Roo.form.NumberField
40484  * @extends Roo.form.TextField
40485  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40486  * @constructor
40487  * Creates a new NumberField
40488  * @param {Object} config Configuration options
40489  */
40490 Roo.form.NumberField = function(config){
40491     Roo.form.NumberField.superclass.constructor.call(this, config);
40492 };
40493
40494 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40495     /**
40496      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40497      */
40498     fieldClass: "x-form-field x-form-num-field",
40499     /**
40500      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40501      */
40502     allowDecimals : true,
40503     /**
40504      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40505      */
40506     decimalSeparator : ".",
40507     /**
40508      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40509      */
40510     decimalPrecision : 2,
40511     /**
40512      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40513      */
40514     allowNegative : true,
40515     /**
40516      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40517      */
40518     minValue : Number.NEGATIVE_INFINITY,
40519     /**
40520      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40521      */
40522     maxValue : Number.MAX_VALUE,
40523     /**
40524      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40525      */
40526     minText : "The minimum value for this field is {0}",
40527     /**
40528      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40529      */
40530     maxText : "The maximum value for this field is {0}",
40531     /**
40532      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40533      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40534      */
40535     nanText : "{0} is not a valid number",
40536
40537     // private
40538     initEvents : function(){
40539         Roo.form.NumberField.superclass.initEvents.call(this);
40540         var allowed = "0123456789";
40541         if(this.allowDecimals){
40542             allowed += this.decimalSeparator;
40543         }
40544         if(this.allowNegative){
40545             allowed += "-";
40546         }
40547         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40548         var keyPress = function(e){
40549             var k = e.getKey();
40550             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40551                 return;
40552             }
40553             var c = e.getCharCode();
40554             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40555                 e.stopEvent();
40556             }
40557         };
40558         this.el.on("keypress", keyPress, this);
40559     },
40560
40561     // private
40562     validateValue : function(value){
40563         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40564             return false;
40565         }
40566         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40567              return true;
40568         }
40569         var num = this.parseValue(value);
40570         if(isNaN(num)){
40571             this.markInvalid(String.format(this.nanText, value));
40572             return false;
40573         }
40574         if(num < this.minValue){
40575             this.markInvalid(String.format(this.minText, this.minValue));
40576             return false;
40577         }
40578         if(num > this.maxValue){
40579             this.markInvalid(String.format(this.maxText, this.maxValue));
40580             return false;
40581         }
40582         return true;
40583     },
40584
40585     getValue : function(){
40586         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40587     },
40588
40589     // private
40590     parseValue : function(value){
40591         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40592         return isNaN(value) ? '' : value;
40593     },
40594
40595     // private
40596     fixPrecision : function(value){
40597         var nan = isNaN(value);
40598         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40599             return nan ? '' : value;
40600         }
40601         return parseFloat(value).toFixed(this.decimalPrecision);
40602     },
40603
40604     setValue : function(v){
40605         v = this.fixPrecision(v);
40606         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40607     },
40608
40609     // private
40610     decimalPrecisionFcn : function(v){
40611         return Math.floor(v);
40612     },
40613
40614     beforeBlur : function(){
40615         var v = this.parseValue(this.getRawValue());
40616         if(v){
40617             this.setValue(v);
40618         }
40619     }
40620 });/*
40621  * Based on:
40622  * Ext JS Library 1.1.1
40623  * Copyright(c) 2006-2007, Ext JS, LLC.
40624  *
40625  * Originally Released Under LGPL - original licence link has changed is not relivant.
40626  *
40627  * Fork - LGPL
40628  * <script type="text/javascript">
40629  */
40630  
40631 /**
40632  * @class Roo.form.DateField
40633  * @extends Roo.form.TriggerField
40634  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40635 * @constructor
40636 * Create a new DateField
40637 * @param {Object} config
40638  */
40639 Roo.form.DateField = function(config)
40640 {
40641     Roo.form.DateField.superclass.constructor.call(this, config);
40642     
40643       this.addEvents({
40644          
40645         /**
40646          * @event select
40647          * Fires when a date is selected
40648              * @param {Roo.form.DateField} combo This combo box
40649              * @param {Date} date The date selected
40650              */
40651         'select' : true
40652          
40653     });
40654     
40655     
40656     if(typeof this.minValue == "string") {
40657         this.minValue = this.parseDate(this.minValue);
40658     }
40659     if(typeof this.maxValue == "string") {
40660         this.maxValue = this.parseDate(this.maxValue);
40661     }
40662     this.ddMatch = null;
40663     if(this.disabledDates){
40664         var dd = this.disabledDates;
40665         var re = "(?:";
40666         for(var i = 0; i < dd.length; i++){
40667             re += dd[i];
40668             if(i != dd.length-1) {
40669                 re += "|";
40670             }
40671         }
40672         this.ddMatch = new RegExp(re + ")");
40673     }
40674 };
40675
40676 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40677     /**
40678      * @cfg {String} format
40679      * The default date format string which can be overriden for localization support.  The format must be
40680      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40681      */
40682     format : "m/d/y",
40683     /**
40684      * @cfg {String} altFormats
40685      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40686      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40687      */
40688     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40689     /**
40690      * @cfg {Array} disabledDays
40691      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40692      */
40693     disabledDays : null,
40694     /**
40695      * @cfg {String} disabledDaysText
40696      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40697      */
40698     disabledDaysText : "Disabled",
40699     /**
40700      * @cfg {Array} disabledDates
40701      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40702      * expression so they are very powerful. Some examples:
40703      * <ul>
40704      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40705      * <li>["03/08", "09/16"] would disable those days for every year</li>
40706      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40707      * <li>["03/../2006"] would disable every day in March 2006</li>
40708      * <li>["^03"] would disable every day in every March</li>
40709      * </ul>
40710      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40711      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40712      */
40713     disabledDates : null,
40714     /**
40715      * @cfg {String} disabledDatesText
40716      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40717      */
40718     disabledDatesText : "Disabled",
40719     /**
40720      * @cfg {Date/String} minValue
40721      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40722      * valid format (defaults to null).
40723      */
40724     minValue : null,
40725     /**
40726      * @cfg {Date/String} maxValue
40727      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40728      * valid format (defaults to null).
40729      */
40730     maxValue : null,
40731     /**
40732      * @cfg {String} minText
40733      * The error text to display when the date in the cell is before minValue (defaults to
40734      * 'The date in this field must be after {minValue}').
40735      */
40736     minText : "The date in this field must be equal to or after {0}",
40737     /**
40738      * @cfg {String} maxText
40739      * The error text to display when the date in the cell is after maxValue (defaults to
40740      * 'The date in this field must be before {maxValue}').
40741      */
40742     maxText : "The date in this field must be equal to or before {0}",
40743     /**
40744      * @cfg {String} invalidText
40745      * The error text to display when the date in the field is invalid (defaults to
40746      * '{value} is not a valid date - it must be in the format {format}').
40747      */
40748     invalidText : "{0} is not a valid date - it must be in the format {1}",
40749     /**
40750      * @cfg {String} triggerClass
40751      * An additional CSS class used to style the trigger button.  The trigger will always get the
40752      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40753      * which displays a calendar icon).
40754      */
40755     triggerClass : 'x-form-date-trigger',
40756     
40757
40758     /**
40759      * @cfg {Boolean} useIso
40760      * if enabled, then the date field will use a hidden field to store the 
40761      * real value as iso formated date. default (false)
40762      */ 
40763     useIso : false,
40764     /**
40765      * @cfg {String/Object} autoCreate
40766      * A DomHelper element spec, or true for a default element spec (defaults to
40767      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40768      */ 
40769     // private
40770     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40771     
40772     // private
40773     hiddenField: false,
40774     
40775     onRender : function(ct, position)
40776     {
40777         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40778         if (this.useIso) {
40779             //this.el.dom.removeAttribute('name'); 
40780             Roo.log("Changing name?");
40781             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40782             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40783                     'before', true);
40784             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40785             // prevent input submission
40786             this.hiddenName = this.name;
40787         }
40788             
40789             
40790     },
40791     
40792     // private
40793     validateValue : function(value)
40794     {
40795         value = this.formatDate(value);
40796         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40797             Roo.log('super failed');
40798             return false;
40799         }
40800         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40801              return true;
40802         }
40803         var svalue = value;
40804         value = this.parseDate(value);
40805         if(!value){
40806             Roo.log('parse date failed' + svalue);
40807             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40808             return false;
40809         }
40810         var time = value.getTime();
40811         if(this.minValue && time < this.minValue.getTime()){
40812             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40813             return false;
40814         }
40815         if(this.maxValue && time > this.maxValue.getTime()){
40816             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40817             return false;
40818         }
40819         if(this.disabledDays){
40820             var day = value.getDay();
40821             for(var i = 0; i < this.disabledDays.length; i++) {
40822                 if(day === this.disabledDays[i]){
40823                     this.markInvalid(this.disabledDaysText);
40824                     return false;
40825                 }
40826             }
40827         }
40828         var fvalue = this.formatDate(value);
40829         if(this.ddMatch && this.ddMatch.test(fvalue)){
40830             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40831             return false;
40832         }
40833         return true;
40834     },
40835
40836     // private
40837     // Provides logic to override the default TriggerField.validateBlur which just returns true
40838     validateBlur : function(){
40839         return !this.menu || !this.menu.isVisible();
40840     },
40841     
40842     getName: function()
40843     {
40844         // returns hidden if it's set..
40845         if (!this.rendered) {return ''};
40846         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40847         
40848     },
40849
40850     /**
40851      * Returns the current date value of the date field.
40852      * @return {Date} The date value
40853      */
40854     getValue : function(){
40855         
40856         return  this.hiddenField ?
40857                 this.hiddenField.value :
40858                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40859     },
40860
40861     /**
40862      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40863      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40864      * (the default format used is "m/d/y").
40865      * <br />Usage:
40866      * <pre><code>
40867 //All of these calls set the same date value (May 4, 2006)
40868
40869 //Pass a date object:
40870 var dt = new Date('5/4/06');
40871 dateField.setValue(dt);
40872
40873 //Pass a date string (default format):
40874 dateField.setValue('5/4/06');
40875
40876 //Pass a date string (custom format):
40877 dateField.format = 'Y-m-d';
40878 dateField.setValue('2006-5-4');
40879 </code></pre>
40880      * @param {String/Date} date The date or valid date string
40881      */
40882     setValue : function(date){
40883         if (this.hiddenField) {
40884             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40885         }
40886         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40887         // make sure the value field is always stored as a date..
40888         this.value = this.parseDate(date);
40889         
40890         
40891     },
40892
40893     // private
40894     parseDate : function(value){
40895         if(!value || value instanceof Date){
40896             return value;
40897         }
40898         var v = Date.parseDate(value, this.format);
40899          if (!v && this.useIso) {
40900             v = Date.parseDate(value, 'Y-m-d');
40901         }
40902         if(!v && this.altFormats){
40903             if(!this.altFormatsArray){
40904                 this.altFormatsArray = this.altFormats.split("|");
40905             }
40906             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40907                 v = Date.parseDate(value, this.altFormatsArray[i]);
40908             }
40909         }
40910         return v;
40911     },
40912
40913     // private
40914     formatDate : function(date, fmt){
40915         return (!date || !(date instanceof Date)) ?
40916                date : date.dateFormat(fmt || this.format);
40917     },
40918
40919     // private
40920     menuListeners : {
40921         select: function(m, d){
40922             
40923             this.setValue(d);
40924             this.fireEvent('select', this, d);
40925         },
40926         show : function(){ // retain focus styling
40927             this.onFocus();
40928         },
40929         hide : function(){
40930             this.focus.defer(10, this);
40931             var ml = this.menuListeners;
40932             this.menu.un("select", ml.select,  this);
40933             this.menu.un("show", ml.show,  this);
40934             this.menu.un("hide", ml.hide,  this);
40935         }
40936     },
40937
40938     // private
40939     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40940     onTriggerClick : function(){
40941         if(this.disabled){
40942             return;
40943         }
40944         if(this.menu == null){
40945             this.menu = new Roo.menu.DateMenu();
40946         }
40947         Roo.apply(this.menu.picker,  {
40948             showClear: this.allowBlank,
40949             minDate : this.minValue,
40950             maxDate : this.maxValue,
40951             disabledDatesRE : this.ddMatch,
40952             disabledDatesText : this.disabledDatesText,
40953             disabledDays : this.disabledDays,
40954             disabledDaysText : this.disabledDaysText,
40955             format : this.useIso ? 'Y-m-d' : this.format,
40956             minText : String.format(this.minText, this.formatDate(this.minValue)),
40957             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40958         });
40959         this.menu.on(Roo.apply({}, this.menuListeners, {
40960             scope:this
40961         }));
40962         this.menu.picker.setValue(this.getValue() || new Date());
40963         this.menu.show(this.el, "tl-bl?");
40964     },
40965
40966     beforeBlur : function(){
40967         var v = this.parseDate(this.getRawValue());
40968         if(v){
40969             this.setValue(v);
40970         }
40971     },
40972
40973     /*@
40974      * overide
40975      * 
40976      */
40977     isDirty : function() {
40978         if(this.disabled) {
40979             return false;
40980         }
40981         
40982         if(typeof(this.startValue) === 'undefined'){
40983             return false;
40984         }
40985         
40986         return String(this.getValue()) !== String(this.startValue);
40987         
40988     },
40989     // @overide
40990     cleanLeadingSpace : function(e)
40991     {
40992        return;
40993     }
40994     
40995 });/*
40996  * Based on:
40997  * Ext JS Library 1.1.1
40998  * Copyright(c) 2006-2007, Ext JS, LLC.
40999  *
41000  * Originally Released Under LGPL - original licence link has changed is not relivant.
41001  *
41002  * Fork - LGPL
41003  * <script type="text/javascript">
41004  */
41005  
41006 /**
41007  * @class Roo.form.MonthField
41008  * @extends Roo.form.TriggerField
41009  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41010 * @constructor
41011 * Create a new MonthField
41012 * @param {Object} config
41013  */
41014 Roo.form.MonthField = function(config){
41015     
41016     Roo.form.MonthField.superclass.constructor.call(this, config);
41017     
41018       this.addEvents({
41019          
41020         /**
41021          * @event select
41022          * Fires when a date is selected
41023              * @param {Roo.form.MonthFieeld} combo This combo box
41024              * @param {Date} date The date selected
41025              */
41026         'select' : true
41027          
41028     });
41029     
41030     
41031     if(typeof this.minValue == "string") {
41032         this.minValue = this.parseDate(this.minValue);
41033     }
41034     if(typeof this.maxValue == "string") {
41035         this.maxValue = this.parseDate(this.maxValue);
41036     }
41037     this.ddMatch = null;
41038     if(this.disabledDates){
41039         var dd = this.disabledDates;
41040         var re = "(?:";
41041         for(var i = 0; i < dd.length; i++){
41042             re += dd[i];
41043             if(i != dd.length-1) {
41044                 re += "|";
41045             }
41046         }
41047         this.ddMatch = new RegExp(re + ")");
41048     }
41049 };
41050
41051 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41052     /**
41053      * @cfg {String} format
41054      * The default date format string which can be overriden for localization support.  The format must be
41055      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41056      */
41057     format : "M Y",
41058     /**
41059      * @cfg {String} altFormats
41060      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41061      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41062      */
41063     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41064     /**
41065      * @cfg {Array} disabledDays
41066      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41067      */
41068     disabledDays : [0,1,2,3,4,5,6],
41069     /**
41070      * @cfg {String} disabledDaysText
41071      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41072      */
41073     disabledDaysText : "Disabled",
41074     /**
41075      * @cfg {Array} disabledDates
41076      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41077      * expression so they are very powerful. Some examples:
41078      * <ul>
41079      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41080      * <li>["03/08", "09/16"] would disable those days for every year</li>
41081      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41082      * <li>["03/../2006"] would disable every day in March 2006</li>
41083      * <li>["^03"] would disable every day in every March</li>
41084      * </ul>
41085      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41086      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41087      */
41088     disabledDates : null,
41089     /**
41090      * @cfg {String} disabledDatesText
41091      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41092      */
41093     disabledDatesText : "Disabled",
41094     /**
41095      * @cfg {Date/String} minValue
41096      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41097      * valid format (defaults to null).
41098      */
41099     minValue : null,
41100     /**
41101      * @cfg {Date/String} maxValue
41102      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41103      * valid format (defaults to null).
41104      */
41105     maxValue : null,
41106     /**
41107      * @cfg {String} minText
41108      * The error text to display when the date in the cell is before minValue (defaults to
41109      * 'The date in this field must be after {minValue}').
41110      */
41111     minText : "The date in this field must be equal to or after {0}",
41112     /**
41113      * @cfg {String} maxTextf
41114      * The error text to display when the date in the cell is after maxValue (defaults to
41115      * 'The date in this field must be before {maxValue}').
41116      */
41117     maxText : "The date in this field must be equal to or before {0}",
41118     /**
41119      * @cfg {String} invalidText
41120      * The error text to display when the date in the field is invalid (defaults to
41121      * '{value} is not a valid date - it must be in the format {format}').
41122      */
41123     invalidText : "{0} is not a valid date - it must be in the format {1}",
41124     /**
41125      * @cfg {String} triggerClass
41126      * An additional CSS class used to style the trigger button.  The trigger will always get the
41127      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41128      * which displays a calendar icon).
41129      */
41130     triggerClass : 'x-form-date-trigger',
41131     
41132
41133     /**
41134      * @cfg {Boolean} useIso
41135      * if enabled, then the date field will use a hidden field to store the 
41136      * real value as iso formated date. default (true)
41137      */ 
41138     useIso : true,
41139     /**
41140      * @cfg {String/Object} autoCreate
41141      * A DomHelper element spec, or true for a default element spec (defaults to
41142      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41143      */ 
41144     // private
41145     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41146     
41147     // private
41148     hiddenField: false,
41149     
41150     hideMonthPicker : false,
41151     
41152     onRender : function(ct, position)
41153     {
41154         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41155         if (this.useIso) {
41156             this.el.dom.removeAttribute('name'); 
41157             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41158                     'before', true);
41159             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41160             // prevent input submission
41161             this.hiddenName = this.name;
41162         }
41163             
41164             
41165     },
41166     
41167     // private
41168     validateValue : function(value)
41169     {
41170         value = this.formatDate(value);
41171         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41172             return false;
41173         }
41174         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41175              return true;
41176         }
41177         var svalue = value;
41178         value = this.parseDate(value);
41179         if(!value){
41180             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41181             return false;
41182         }
41183         var time = value.getTime();
41184         if(this.minValue && time < this.minValue.getTime()){
41185             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41186             return false;
41187         }
41188         if(this.maxValue && time > this.maxValue.getTime()){
41189             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41190             return false;
41191         }
41192         /*if(this.disabledDays){
41193             var day = value.getDay();
41194             for(var i = 0; i < this.disabledDays.length; i++) {
41195                 if(day === this.disabledDays[i]){
41196                     this.markInvalid(this.disabledDaysText);
41197                     return false;
41198                 }
41199             }
41200         }
41201         */
41202         var fvalue = this.formatDate(value);
41203         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41204             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41205             return false;
41206         }
41207         */
41208         return true;
41209     },
41210
41211     // private
41212     // Provides logic to override the default TriggerField.validateBlur which just returns true
41213     validateBlur : function(){
41214         return !this.menu || !this.menu.isVisible();
41215     },
41216
41217     /**
41218      * Returns the current date value of the date field.
41219      * @return {Date} The date value
41220      */
41221     getValue : function(){
41222         
41223         
41224         
41225         return  this.hiddenField ?
41226                 this.hiddenField.value :
41227                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41228     },
41229
41230     /**
41231      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41232      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41233      * (the default format used is "m/d/y").
41234      * <br />Usage:
41235      * <pre><code>
41236 //All of these calls set the same date value (May 4, 2006)
41237
41238 //Pass a date object:
41239 var dt = new Date('5/4/06');
41240 monthField.setValue(dt);
41241
41242 //Pass a date string (default format):
41243 monthField.setValue('5/4/06');
41244
41245 //Pass a date string (custom format):
41246 monthField.format = 'Y-m-d';
41247 monthField.setValue('2006-5-4');
41248 </code></pre>
41249      * @param {String/Date} date The date or valid date string
41250      */
41251     setValue : function(date){
41252         Roo.log('month setValue' + date);
41253         // can only be first of month..
41254         
41255         var val = this.parseDate(date);
41256         
41257         if (this.hiddenField) {
41258             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41259         }
41260         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41261         this.value = this.parseDate(date);
41262     },
41263
41264     // private
41265     parseDate : function(value){
41266         if(!value || value instanceof Date){
41267             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41268             return value;
41269         }
41270         var v = Date.parseDate(value, this.format);
41271         if (!v && this.useIso) {
41272             v = Date.parseDate(value, 'Y-m-d');
41273         }
41274         if (v) {
41275             // 
41276             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41277         }
41278         
41279         
41280         if(!v && this.altFormats){
41281             if(!this.altFormatsArray){
41282                 this.altFormatsArray = this.altFormats.split("|");
41283             }
41284             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41285                 v = Date.parseDate(value, this.altFormatsArray[i]);
41286             }
41287         }
41288         return v;
41289     },
41290
41291     // private
41292     formatDate : function(date, fmt){
41293         return (!date || !(date instanceof Date)) ?
41294                date : date.dateFormat(fmt || this.format);
41295     },
41296
41297     // private
41298     menuListeners : {
41299         select: function(m, d){
41300             this.setValue(d);
41301             this.fireEvent('select', this, d);
41302         },
41303         show : function(){ // retain focus styling
41304             this.onFocus();
41305         },
41306         hide : function(){
41307             this.focus.defer(10, this);
41308             var ml = this.menuListeners;
41309             this.menu.un("select", ml.select,  this);
41310             this.menu.un("show", ml.show,  this);
41311             this.menu.un("hide", ml.hide,  this);
41312         }
41313     },
41314     // private
41315     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41316     onTriggerClick : function(){
41317         if(this.disabled){
41318             return;
41319         }
41320         if(this.menu == null){
41321             this.menu = new Roo.menu.DateMenu();
41322            
41323         }
41324         
41325         Roo.apply(this.menu.picker,  {
41326             
41327             showClear: this.allowBlank,
41328             minDate : this.minValue,
41329             maxDate : this.maxValue,
41330             disabledDatesRE : this.ddMatch,
41331             disabledDatesText : this.disabledDatesText,
41332             
41333             format : this.useIso ? 'Y-m-d' : this.format,
41334             minText : String.format(this.minText, this.formatDate(this.minValue)),
41335             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41336             
41337         });
41338          this.menu.on(Roo.apply({}, this.menuListeners, {
41339             scope:this
41340         }));
41341        
41342         
41343         var m = this.menu;
41344         var p = m.picker;
41345         
41346         // hide month picker get's called when we called by 'before hide';
41347         
41348         var ignorehide = true;
41349         p.hideMonthPicker  = function(disableAnim){
41350             if (ignorehide) {
41351                 return;
41352             }
41353              if(this.monthPicker){
41354                 Roo.log("hideMonthPicker called");
41355                 if(disableAnim === true){
41356                     this.monthPicker.hide();
41357                 }else{
41358                     this.monthPicker.slideOut('t', {duration:.2});
41359                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41360                     p.fireEvent("select", this, this.value);
41361                     m.hide();
41362                 }
41363             }
41364         }
41365         
41366         Roo.log('picker set value');
41367         Roo.log(this.getValue());
41368         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41369         m.show(this.el, 'tl-bl?');
41370         ignorehide  = false;
41371         // this will trigger hideMonthPicker..
41372         
41373         
41374         // hidden the day picker
41375         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41376         
41377         
41378         
41379       
41380         
41381         p.showMonthPicker.defer(100, p);
41382     
41383         
41384        
41385     },
41386
41387     beforeBlur : function(){
41388         var v = this.parseDate(this.getRawValue());
41389         if(v){
41390             this.setValue(v);
41391         }
41392     }
41393
41394     /** @cfg {Boolean} grow @hide */
41395     /** @cfg {Number} growMin @hide */
41396     /** @cfg {Number} growMax @hide */
41397     /**
41398      * @hide
41399      * @method autoSize
41400      */
41401 });/*
41402  * Based on:
41403  * Ext JS Library 1.1.1
41404  * Copyright(c) 2006-2007, Ext JS, LLC.
41405  *
41406  * Originally Released Under LGPL - original licence link has changed is not relivant.
41407  *
41408  * Fork - LGPL
41409  * <script type="text/javascript">
41410  */
41411  
41412
41413 /**
41414  * @class Roo.form.ComboBox
41415  * @extends Roo.form.TriggerField
41416  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41417  * @constructor
41418  * Create a new ComboBox.
41419  * @param {Object} config Configuration options
41420  */
41421 Roo.form.ComboBox = function(config){
41422     Roo.form.ComboBox.superclass.constructor.call(this, config);
41423     this.addEvents({
41424         /**
41425          * @event expand
41426          * Fires when the dropdown list is expanded
41427              * @param {Roo.form.ComboBox} combo This combo box
41428              */
41429         'expand' : true,
41430         /**
41431          * @event collapse
41432          * Fires when the dropdown list is collapsed
41433              * @param {Roo.form.ComboBox} combo This combo box
41434              */
41435         'collapse' : true,
41436         /**
41437          * @event beforeselect
41438          * Fires before a list item is selected. Return false to cancel the selection.
41439              * @param {Roo.form.ComboBox} combo This combo box
41440              * @param {Roo.data.Record} record The data record returned from the underlying store
41441              * @param {Number} index The index of the selected item in the dropdown list
41442              */
41443         'beforeselect' : true,
41444         /**
41445          * @event select
41446          * Fires when a list item is selected
41447              * @param {Roo.form.ComboBox} combo This combo box
41448              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41449              * @param {Number} index The index of the selected item in the dropdown list
41450              */
41451         'select' : true,
41452         /**
41453          * @event beforequery
41454          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41455          * The event object passed has these properties:
41456              * @param {Roo.form.ComboBox} combo This combo box
41457              * @param {String} query The query
41458              * @param {Boolean} forceAll true to force "all" query
41459              * @param {Boolean} cancel true to cancel the query
41460              * @param {Object} e The query event object
41461              */
41462         'beforequery': true,
41463          /**
41464          * @event add
41465          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41466              * @param {Roo.form.ComboBox} combo This combo box
41467              */
41468         'add' : true,
41469         /**
41470          * @event edit
41471          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41472              * @param {Roo.form.ComboBox} combo This combo box
41473              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41474              */
41475         'edit' : true
41476         
41477         
41478     });
41479     if(this.transform){
41480         this.allowDomMove = false;
41481         var s = Roo.getDom(this.transform);
41482         if(!this.hiddenName){
41483             this.hiddenName = s.name;
41484         }
41485         if(!this.store){
41486             this.mode = 'local';
41487             var d = [], opts = s.options;
41488             for(var i = 0, len = opts.length;i < len; i++){
41489                 var o = opts[i];
41490                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41491                 if(o.selected) {
41492                     this.value = value;
41493                 }
41494                 d.push([value, o.text]);
41495             }
41496             this.store = new Roo.data.SimpleStore({
41497                 'id': 0,
41498                 fields: ['value', 'text'],
41499                 data : d
41500             });
41501             this.valueField = 'value';
41502             this.displayField = 'text';
41503         }
41504         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41505         if(!this.lazyRender){
41506             this.target = true;
41507             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41508             s.parentNode.removeChild(s); // remove it
41509             this.render(this.el.parentNode);
41510         }else{
41511             s.parentNode.removeChild(s); // remove it
41512         }
41513
41514     }
41515     if (this.store) {
41516         this.store = Roo.factory(this.store, Roo.data);
41517     }
41518     
41519     this.selectedIndex = -1;
41520     if(this.mode == 'local'){
41521         if(config.queryDelay === undefined){
41522             this.queryDelay = 10;
41523         }
41524         if(config.minChars === undefined){
41525             this.minChars = 0;
41526         }
41527     }
41528 };
41529
41530 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41531     /**
41532      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41533      */
41534     /**
41535      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41536      * rendering into an Roo.Editor, defaults to false)
41537      */
41538     /**
41539      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41540      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41541      */
41542     /**
41543      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41544      */
41545     /**
41546      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41547      * the dropdown list (defaults to undefined, with no header element)
41548      */
41549
41550      /**
41551      * @cfg {String/Roo.Template} tpl The template to use to render the output
41552      */
41553      
41554     // private
41555     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41556     /**
41557      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41558      */
41559     listWidth: undefined,
41560     /**
41561      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41562      * mode = 'remote' or 'text' if mode = 'local')
41563      */
41564     displayField: undefined,
41565     /**
41566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41567      * mode = 'remote' or 'value' if mode = 'local'). 
41568      * Note: use of a valueField requires the user make a selection
41569      * in order for a value to be mapped.
41570      */
41571     valueField: undefined,
41572     
41573     
41574     /**
41575      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41576      * field's data value (defaults to the underlying DOM element's name)
41577      */
41578     hiddenName: undefined,
41579     /**
41580      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41581      */
41582     listClass: '',
41583     /**
41584      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41585      */
41586     selectedClass: 'x-combo-selected',
41587     /**
41588      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41589      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41590      * which displays a downward arrow icon).
41591      */
41592     triggerClass : 'x-form-arrow-trigger',
41593     /**
41594      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41595      */
41596     shadow:'sides',
41597     /**
41598      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41599      * anchor positions (defaults to 'tl-bl')
41600      */
41601     listAlign: 'tl-bl?',
41602     /**
41603      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41604      */
41605     maxHeight: 300,
41606     /**
41607      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41608      * query specified by the allQuery config option (defaults to 'query')
41609      */
41610     triggerAction: 'query',
41611     /**
41612      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41613      * (defaults to 4, does not apply if editable = false)
41614      */
41615     minChars : 4,
41616     /**
41617      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41618      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41619      */
41620     typeAhead: false,
41621     /**
41622      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41623      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41624      */
41625     queryDelay: 500,
41626     /**
41627      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41628      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41629      */
41630     pageSize: 0,
41631     /**
41632      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41633      * when editable = true (defaults to false)
41634      */
41635     selectOnFocus:false,
41636     /**
41637      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41638      */
41639     queryParam: 'query',
41640     /**
41641      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41642      * when mode = 'remote' (defaults to 'Loading...')
41643      */
41644     loadingText: 'Loading...',
41645     /**
41646      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41647      */
41648     resizable: false,
41649     /**
41650      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41651      */
41652     handleHeight : 8,
41653     /**
41654      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41655      * traditional select (defaults to true)
41656      */
41657     editable: true,
41658     /**
41659      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41660      */
41661     allQuery: '',
41662     /**
41663      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41664      */
41665     mode: 'remote',
41666     /**
41667      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41668      * listWidth has a higher value)
41669      */
41670     minListWidth : 70,
41671     /**
41672      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41673      * allow the user to set arbitrary text into the field (defaults to false)
41674      */
41675     forceSelection:false,
41676     /**
41677      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41678      * if typeAhead = true (defaults to 250)
41679      */
41680     typeAheadDelay : 250,
41681     /**
41682      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41683      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41684      */
41685     valueNotFoundText : undefined,
41686     /**
41687      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41688      */
41689     blockFocus : false,
41690     
41691     /**
41692      * @cfg {Boolean} disableClear Disable showing of clear button.
41693      */
41694     disableClear : false,
41695     /**
41696      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41697      */
41698     alwaysQuery : false,
41699     
41700     //private
41701     addicon : false,
41702     editicon: false,
41703     
41704     // element that contains real text value.. (when hidden is used..)
41705      
41706     // private
41707     onRender : function(ct, position)
41708     {
41709         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41710         
41711         if(this.hiddenName){
41712             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41713                     'before', true);
41714             this.hiddenField.value =
41715                 this.hiddenValue !== undefined ? this.hiddenValue :
41716                 this.value !== undefined ? this.value : '';
41717
41718             // prevent input submission
41719             this.el.dom.removeAttribute('name');
41720              
41721              
41722         }
41723         
41724         if(Roo.isGecko){
41725             this.el.dom.setAttribute('autocomplete', 'off');
41726         }
41727
41728         var cls = 'x-combo-list';
41729
41730         this.list = new Roo.Layer({
41731             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41732         });
41733
41734         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41735         this.list.setWidth(lw);
41736         this.list.swallowEvent('mousewheel');
41737         this.assetHeight = 0;
41738
41739         if(this.title){
41740             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41741             this.assetHeight += this.header.getHeight();
41742         }
41743
41744         this.innerList = this.list.createChild({cls:cls+'-inner'});
41745         this.innerList.on('mouseover', this.onViewOver, this);
41746         this.innerList.on('mousemove', this.onViewMove, this);
41747         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41748         
41749         if(this.allowBlank && !this.pageSize && !this.disableClear){
41750             this.footer = this.list.createChild({cls:cls+'-ft'});
41751             this.pageTb = new Roo.Toolbar(this.footer);
41752            
41753         }
41754         if(this.pageSize){
41755             this.footer = this.list.createChild({cls:cls+'-ft'});
41756             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41757                     {pageSize: this.pageSize});
41758             
41759         }
41760         
41761         if (this.pageTb && this.allowBlank && !this.disableClear) {
41762             var _this = this;
41763             this.pageTb.add(new Roo.Toolbar.Fill(), {
41764                 cls: 'x-btn-icon x-btn-clear',
41765                 text: '&#160;',
41766                 handler: function()
41767                 {
41768                     _this.collapse();
41769                     _this.clearValue();
41770                     _this.onSelect(false, -1);
41771                 }
41772             });
41773         }
41774         if (this.footer) {
41775             this.assetHeight += this.footer.getHeight();
41776         }
41777         
41778
41779         if(!this.tpl){
41780             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41781         }
41782
41783         this.view = new Roo.View(this.innerList, this.tpl, {
41784             singleSelect:true,
41785             store: this.store,
41786             selectedClass: this.selectedClass
41787         });
41788
41789         this.view.on('click', this.onViewClick, this);
41790
41791         this.store.on('beforeload', this.onBeforeLoad, this);
41792         this.store.on('load', this.onLoad, this);
41793         this.store.on('loadexception', this.onLoadException, this);
41794
41795         if(this.resizable){
41796             this.resizer = new Roo.Resizable(this.list,  {
41797                pinned:true, handles:'se'
41798             });
41799             this.resizer.on('resize', function(r, w, h){
41800                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41801                 this.listWidth = w;
41802                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41803                 this.restrictHeight();
41804             }, this);
41805             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41806         }
41807         if(!this.editable){
41808             this.editable = true;
41809             this.setEditable(false);
41810         }  
41811         
41812         
41813         if (typeof(this.events.add.listeners) != 'undefined') {
41814             
41815             this.addicon = this.wrap.createChild(
41816                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41817        
41818             this.addicon.on('click', function(e) {
41819                 this.fireEvent('add', this);
41820             }, this);
41821         }
41822         if (typeof(this.events.edit.listeners) != 'undefined') {
41823             
41824             this.editicon = this.wrap.createChild(
41825                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41826             if (this.addicon) {
41827                 this.editicon.setStyle('margin-left', '40px');
41828             }
41829             this.editicon.on('click', function(e) {
41830                 
41831                 // we fire even  if inothing is selected..
41832                 this.fireEvent('edit', this, this.lastData );
41833                 
41834             }, this);
41835         }
41836         
41837         
41838         
41839     },
41840
41841     // private
41842     initEvents : function(){
41843         Roo.form.ComboBox.superclass.initEvents.call(this);
41844
41845         this.keyNav = new Roo.KeyNav(this.el, {
41846             "up" : function(e){
41847                 this.inKeyMode = true;
41848                 this.selectPrev();
41849             },
41850
41851             "down" : function(e){
41852                 if(!this.isExpanded()){
41853                     this.onTriggerClick();
41854                 }else{
41855                     this.inKeyMode = true;
41856                     this.selectNext();
41857                 }
41858             },
41859
41860             "enter" : function(e){
41861                 this.onViewClick();
41862                 //return true;
41863             },
41864
41865             "esc" : function(e){
41866                 this.collapse();
41867             },
41868
41869             "tab" : function(e){
41870                 this.onViewClick(false);
41871                 this.fireEvent("specialkey", this, e);
41872                 return true;
41873             },
41874
41875             scope : this,
41876
41877             doRelay : function(foo, bar, hname){
41878                 if(hname == 'down' || this.scope.isExpanded()){
41879                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41880                 }
41881                 return true;
41882             },
41883
41884             forceKeyDown: true
41885         });
41886         this.queryDelay = Math.max(this.queryDelay || 10,
41887                 this.mode == 'local' ? 10 : 250);
41888         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41889         if(this.typeAhead){
41890             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41891         }
41892         if(this.editable !== false){
41893             this.el.on("keyup", this.onKeyUp, this);
41894         }
41895         if(this.forceSelection){
41896             this.on('blur', this.doForce, this);
41897         }
41898     },
41899
41900     onDestroy : function(){
41901         if(this.view){
41902             this.view.setStore(null);
41903             this.view.el.removeAllListeners();
41904             this.view.el.remove();
41905             this.view.purgeListeners();
41906         }
41907         if(this.list){
41908             this.list.destroy();
41909         }
41910         if(this.store){
41911             this.store.un('beforeload', this.onBeforeLoad, this);
41912             this.store.un('load', this.onLoad, this);
41913             this.store.un('loadexception', this.onLoadException, this);
41914         }
41915         Roo.form.ComboBox.superclass.onDestroy.call(this);
41916     },
41917
41918     // private
41919     fireKey : function(e){
41920         if(e.isNavKeyPress() && !this.list.isVisible()){
41921             this.fireEvent("specialkey", this, e);
41922         }
41923     },
41924
41925     // private
41926     onResize: function(w, h){
41927         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41928         
41929         if(typeof w != 'number'){
41930             // we do not handle it!?!?
41931             return;
41932         }
41933         var tw = this.trigger.getWidth();
41934         tw += this.addicon ? this.addicon.getWidth() : 0;
41935         tw += this.editicon ? this.editicon.getWidth() : 0;
41936         var x = w - tw;
41937         this.el.setWidth( this.adjustWidth('input', x));
41938             
41939         this.trigger.setStyle('left', x+'px');
41940         
41941         if(this.list && this.listWidth === undefined){
41942             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41943             this.list.setWidth(lw);
41944             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41945         }
41946         
41947     
41948         
41949     },
41950
41951     /**
41952      * Allow or prevent the user from directly editing the field text.  If false is passed,
41953      * the user will only be able to select from the items defined in the dropdown list.  This method
41954      * is the runtime equivalent of setting the 'editable' config option at config time.
41955      * @param {Boolean} value True to allow the user to directly edit the field text
41956      */
41957     setEditable : function(value){
41958         if(value == this.editable){
41959             return;
41960         }
41961         this.editable = value;
41962         if(!value){
41963             this.el.dom.setAttribute('readOnly', true);
41964             this.el.on('mousedown', this.onTriggerClick,  this);
41965             this.el.addClass('x-combo-noedit');
41966         }else{
41967             this.el.dom.setAttribute('readOnly', false);
41968             this.el.un('mousedown', this.onTriggerClick,  this);
41969             this.el.removeClass('x-combo-noedit');
41970         }
41971     },
41972
41973     // private
41974     onBeforeLoad : function(){
41975         if(!this.hasFocus){
41976             return;
41977         }
41978         this.innerList.update(this.loadingText ?
41979                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41980         this.restrictHeight();
41981         this.selectedIndex = -1;
41982     },
41983
41984     // private
41985     onLoad : function(){
41986         if(!this.hasFocus){
41987             return;
41988         }
41989         if(this.store.getCount() > 0){
41990             this.expand();
41991             this.restrictHeight();
41992             if(this.lastQuery == this.allQuery){
41993                 if(this.editable){
41994                     this.el.dom.select();
41995                 }
41996                 if(!this.selectByValue(this.value, true)){
41997                     this.select(0, true);
41998                 }
41999             }else{
42000                 this.selectNext();
42001                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42002                     this.taTask.delay(this.typeAheadDelay);
42003                 }
42004             }
42005         }else{
42006             this.onEmptyResults();
42007         }
42008         //this.el.focus();
42009     },
42010     // private
42011     onLoadException : function()
42012     {
42013         this.collapse();
42014         Roo.log(this.store.reader.jsonData);
42015         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42016             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42017         }
42018         
42019         
42020     },
42021     // private
42022     onTypeAhead : function(){
42023         if(this.store.getCount() > 0){
42024             var r = this.store.getAt(0);
42025             var newValue = r.data[this.displayField];
42026             var len = newValue.length;
42027             var selStart = this.getRawValue().length;
42028             if(selStart != len){
42029                 this.setRawValue(newValue);
42030                 this.selectText(selStart, newValue.length);
42031             }
42032         }
42033     },
42034
42035     // private
42036     onSelect : function(record, index){
42037         if(this.fireEvent('beforeselect', this, record, index) !== false){
42038             this.setFromData(index > -1 ? record.data : false);
42039             this.collapse();
42040             this.fireEvent('select', this, record, index);
42041         }
42042     },
42043
42044     /**
42045      * Returns the currently selected field value or empty string if no value is set.
42046      * @return {String} value The selected value
42047      */
42048     getValue : function(){
42049         if(this.valueField){
42050             return typeof this.value != 'undefined' ? this.value : '';
42051         }
42052         return Roo.form.ComboBox.superclass.getValue.call(this);
42053     },
42054
42055     /**
42056      * Clears any text/value currently set in the field
42057      */
42058     clearValue : function(){
42059         if(this.hiddenField){
42060             this.hiddenField.value = '';
42061         }
42062         this.value = '';
42063         this.setRawValue('');
42064         this.lastSelectionText = '';
42065         
42066     },
42067
42068     /**
42069      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42070      * will be displayed in the field.  If the value does not match the data value of an existing item,
42071      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42072      * Otherwise the field will be blank (although the value will still be set).
42073      * @param {String} value The value to match
42074      */
42075     setValue : function(v){
42076         var text = v;
42077         if(this.valueField){
42078             var r = this.findRecord(this.valueField, v);
42079             if(r){
42080                 text = r.data[this.displayField];
42081             }else if(this.valueNotFoundText !== undefined){
42082                 text = this.valueNotFoundText;
42083             }
42084         }
42085         this.lastSelectionText = text;
42086         if(this.hiddenField){
42087             this.hiddenField.value = v;
42088         }
42089         Roo.form.ComboBox.superclass.setValue.call(this, text);
42090         this.value = v;
42091     },
42092     /**
42093      * @property {Object} the last set data for the element
42094      */
42095     
42096     lastData : false,
42097     /**
42098      * Sets the value of the field based on a object which is related to the record format for the store.
42099      * @param {Object} value the value to set as. or false on reset?
42100      */
42101     setFromData : function(o){
42102         var dv = ''; // display value
42103         var vv = ''; // value value..
42104         this.lastData = o;
42105         if (this.displayField) {
42106             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42107         } else {
42108             // this is an error condition!!!
42109             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42110         }
42111         
42112         if(this.valueField){
42113             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42114         }
42115         if(this.hiddenField){
42116             this.hiddenField.value = vv;
42117             
42118             this.lastSelectionText = dv;
42119             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42120             this.value = vv;
42121             return;
42122         }
42123         // no hidden field.. - we store the value in 'value', but still display
42124         // display field!!!!
42125         this.lastSelectionText = dv;
42126         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42127         this.value = vv;
42128         
42129         
42130     },
42131     // private
42132     reset : function(){
42133         // overridden so that last data is reset..
42134         this.setValue(this.resetValue);
42135         this.originalValue = this.getValue();
42136         this.clearInvalid();
42137         this.lastData = false;
42138         if (this.view) {
42139             this.view.clearSelections();
42140         }
42141     },
42142     // private
42143     findRecord : function(prop, value){
42144         var record;
42145         if(this.store.getCount() > 0){
42146             this.store.each(function(r){
42147                 if(r.data[prop] == value){
42148                     record = r;
42149                     return false;
42150                 }
42151                 return true;
42152             });
42153         }
42154         return record;
42155     },
42156     
42157     getName: function()
42158     {
42159         // returns hidden if it's set..
42160         if (!this.rendered) {return ''};
42161         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42162         
42163     },
42164     // private
42165     onViewMove : function(e, t){
42166         this.inKeyMode = false;
42167     },
42168
42169     // private
42170     onViewOver : function(e, t){
42171         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42172             return;
42173         }
42174         var item = this.view.findItemFromChild(t);
42175         if(item){
42176             var index = this.view.indexOf(item);
42177             this.select(index, false);
42178         }
42179     },
42180
42181     // private
42182     onViewClick : function(doFocus)
42183     {
42184         var index = this.view.getSelectedIndexes()[0];
42185         var r = this.store.getAt(index);
42186         if(r){
42187             this.onSelect(r, index);
42188         }
42189         if(doFocus !== false && !this.blockFocus){
42190             this.el.focus();
42191         }
42192     },
42193
42194     // private
42195     restrictHeight : function(){
42196         this.innerList.dom.style.height = '';
42197         var inner = this.innerList.dom;
42198         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42199         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42200         this.list.beginUpdate();
42201         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42202         this.list.alignTo(this.el, this.listAlign);
42203         this.list.endUpdate();
42204     },
42205
42206     // private
42207     onEmptyResults : function(){
42208         this.collapse();
42209     },
42210
42211     /**
42212      * Returns true if the dropdown list is expanded, else false.
42213      */
42214     isExpanded : function(){
42215         return this.list.isVisible();
42216     },
42217
42218     /**
42219      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42220      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42221      * @param {String} value The data value of the item to select
42222      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42223      * selected item if it is not currently in view (defaults to true)
42224      * @return {Boolean} True if the value matched an item in the list, else false
42225      */
42226     selectByValue : function(v, scrollIntoView){
42227         if(v !== undefined && v !== null){
42228             var r = this.findRecord(this.valueField || this.displayField, v);
42229             if(r){
42230                 this.select(this.store.indexOf(r), scrollIntoView);
42231                 return true;
42232             }
42233         }
42234         return false;
42235     },
42236
42237     /**
42238      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42239      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42240      * @param {Number} index The zero-based index of the list item to select
42241      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42242      * selected item if it is not currently in view (defaults to true)
42243      */
42244     select : function(index, scrollIntoView){
42245         this.selectedIndex = index;
42246         this.view.select(index);
42247         if(scrollIntoView !== false){
42248             var el = this.view.getNode(index);
42249             if(el){
42250                 this.innerList.scrollChildIntoView(el, false);
42251             }
42252         }
42253     },
42254
42255     // private
42256     selectNext : function(){
42257         var ct = this.store.getCount();
42258         if(ct > 0){
42259             if(this.selectedIndex == -1){
42260                 this.select(0);
42261             }else if(this.selectedIndex < ct-1){
42262                 this.select(this.selectedIndex+1);
42263             }
42264         }
42265     },
42266
42267     // private
42268     selectPrev : function(){
42269         var ct = this.store.getCount();
42270         if(ct > 0){
42271             if(this.selectedIndex == -1){
42272                 this.select(0);
42273             }else if(this.selectedIndex != 0){
42274                 this.select(this.selectedIndex-1);
42275             }
42276         }
42277     },
42278
42279     // private
42280     onKeyUp : function(e){
42281         if(this.editable !== false && !e.isSpecialKey()){
42282             this.lastKey = e.getKey();
42283             this.dqTask.delay(this.queryDelay);
42284         }
42285     },
42286
42287     // private
42288     validateBlur : function(){
42289         return !this.list || !this.list.isVisible();   
42290     },
42291
42292     // private
42293     initQuery : function(){
42294         this.doQuery(this.getRawValue());
42295     },
42296
42297     // private
42298     doForce : function(){
42299         if(this.el.dom.value.length > 0){
42300             this.el.dom.value =
42301                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42302              
42303         }
42304     },
42305
42306     /**
42307      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42308      * query allowing the query action to be canceled if needed.
42309      * @param {String} query The SQL query to execute
42310      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42311      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42312      * saved in the current store (defaults to false)
42313      */
42314     doQuery : function(q, forceAll){
42315         if(q === undefined || q === null){
42316             q = '';
42317         }
42318         var qe = {
42319             query: q,
42320             forceAll: forceAll,
42321             combo: this,
42322             cancel:false
42323         };
42324         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42325             return false;
42326         }
42327         q = qe.query;
42328         forceAll = qe.forceAll;
42329         if(forceAll === true || (q.length >= this.minChars)){
42330             if(this.lastQuery != q || this.alwaysQuery){
42331                 this.lastQuery = q;
42332                 if(this.mode == 'local'){
42333                     this.selectedIndex = -1;
42334                     if(forceAll){
42335                         this.store.clearFilter();
42336                     }else{
42337                         this.store.filter(this.displayField, q);
42338                     }
42339                     this.onLoad();
42340                 }else{
42341                     this.store.baseParams[this.queryParam] = q;
42342                     this.store.load({
42343                         params: this.getParams(q)
42344                     });
42345                     this.expand();
42346                 }
42347             }else{
42348                 this.selectedIndex = -1;
42349                 this.onLoad();   
42350             }
42351         }
42352     },
42353
42354     // private
42355     getParams : function(q){
42356         var p = {};
42357         //p[this.queryParam] = q;
42358         if(this.pageSize){
42359             p.start = 0;
42360             p.limit = this.pageSize;
42361         }
42362         return p;
42363     },
42364
42365     /**
42366      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42367      */
42368     collapse : function(){
42369         if(!this.isExpanded()){
42370             return;
42371         }
42372         this.list.hide();
42373         Roo.get(document).un('mousedown', this.collapseIf, this);
42374         Roo.get(document).un('mousewheel', this.collapseIf, this);
42375         if (!this.editable) {
42376             Roo.get(document).un('keydown', this.listKeyPress, this);
42377         }
42378         this.fireEvent('collapse', this);
42379     },
42380
42381     // private
42382     collapseIf : function(e){
42383         if(!e.within(this.wrap) && !e.within(this.list)){
42384             this.collapse();
42385         }
42386     },
42387
42388     /**
42389      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42390      */
42391     expand : function(){
42392         if(this.isExpanded() || !this.hasFocus){
42393             return;
42394         }
42395         this.list.alignTo(this.el, this.listAlign);
42396         this.list.show();
42397         Roo.get(document).on('mousedown', this.collapseIf, this);
42398         Roo.get(document).on('mousewheel', this.collapseIf, this);
42399         if (!this.editable) {
42400             Roo.get(document).on('keydown', this.listKeyPress, this);
42401         }
42402         
42403         this.fireEvent('expand', this);
42404     },
42405
42406     // private
42407     // Implements the default empty TriggerField.onTriggerClick function
42408     onTriggerClick : function(){
42409         if(this.disabled){
42410             return;
42411         }
42412         if(this.isExpanded()){
42413             this.collapse();
42414             if (!this.blockFocus) {
42415                 this.el.focus();
42416             }
42417             
42418         }else {
42419             this.hasFocus = true;
42420             if(this.triggerAction == 'all') {
42421                 this.doQuery(this.allQuery, true);
42422             } else {
42423                 this.doQuery(this.getRawValue());
42424             }
42425             if (!this.blockFocus) {
42426                 this.el.focus();
42427             }
42428         }
42429     },
42430     listKeyPress : function(e)
42431     {
42432         //Roo.log('listkeypress');
42433         // scroll to first matching element based on key pres..
42434         if (e.isSpecialKey()) {
42435             return false;
42436         }
42437         var k = String.fromCharCode(e.getKey()).toUpperCase();
42438         //Roo.log(k);
42439         var match  = false;
42440         var csel = this.view.getSelectedNodes();
42441         var cselitem = false;
42442         if (csel.length) {
42443             var ix = this.view.indexOf(csel[0]);
42444             cselitem  = this.store.getAt(ix);
42445             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42446                 cselitem = false;
42447             }
42448             
42449         }
42450         
42451         this.store.each(function(v) { 
42452             if (cselitem) {
42453                 // start at existing selection.
42454                 if (cselitem.id == v.id) {
42455                     cselitem = false;
42456                 }
42457                 return;
42458             }
42459                 
42460             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42461                 match = this.store.indexOf(v);
42462                 return false;
42463             }
42464         }, this);
42465         
42466         if (match === false) {
42467             return true; // no more action?
42468         }
42469         // scroll to?
42470         this.view.select(match);
42471         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42472         sn.scrollIntoView(sn.dom.parentNode, false);
42473     } 
42474
42475     /** 
42476     * @cfg {Boolean} grow 
42477     * @hide 
42478     */
42479     /** 
42480     * @cfg {Number} growMin 
42481     * @hide 
42482     */
42483     /** 
42484     * @cfg {Number} growMax 
42485     * @hide 
42486     */
42487     /**
42488      * @hide
42489      * @method autoSize
42490      */
42491 });/*
42492  * Copyright(c) 2010-2012, Roo J Solutions Limited
42493  *
42494  * Licence LGPL
42495  *
42496  */
42497
42498 /**
42499  * @class Roo.form.ComboBoxArray
42500  * @extends Roo.form.TextField
42501  * A facebook style adder... for lists of email / people / countries  etc...
42502  * pick multiple items from a combo box, and shows each one.
42503  *
42504  *  Fred [x]  Brian [x]  [Pick another |v]
42505  *
42506  *
42507  *  For this to work: it needs various extra information
42508  *    - normal combo problay has
42509  *      name, hiddenName
42510  *    + displayField, valueField
42511  *
42512  *    For our purpose...
42513  *
42514  *
42515  *   If we change from 'extends' to wrapping...
42516  *   
42517  *  
42518  *
42519  
42520  
42521  * @constructor
42522  * Create a new ComboBoxArray.
42523  * @param {Object} config Configuration options
42524  */
42525  
42526
42527 Roo.form.ComboBoxArray = function(config)
42528 {
42529     this.addEvents({
42530         /**
42531          * @event beforeremove
42532          * Fires before remove the value from the list
42533              * @param {Roo.form.ComboBoxArray} _self This combo box array
42534              * @param {Roo.form.ComboBoxArray.Item} item removed item
42535              */
42536         'beforeremove' : true,
42537         /**
42538          * @event remove
42539          * Fires when remove the value from the list
42540              * @param {Roo.form.ComboBoxArray} _self This combo box array
42541              * @param {Roo.form.ComboBoxArray.Item} item removed item
42542              */
42543         'remove' : true
42544         
42545         
42546     });
42547     
42548     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42549     
42550     this.items = new Roo.util.MixedCollection(false);
42551     
42552     // construct the child combo...
42553     
42554     
42555     
42556     
42557    
42558     
42559 }
42560
42561  
42562 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42563
42564     /**
42565      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
42566      */
42567     
42568     lastData : false,
42569     
42570     // behavies liek a hiddne field
42571     inputType:      'hidden',
42572     /**
42573      * @cfg {Number} width The width of the box that displays the selected element
42574      */ 
42575     width:          300,
42576
42577     
42578     
42579     /**
42580      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42581      */
42582     name : false,
42583     /**
42584      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42585      */
42586     hiddenName : false,
42587       /**
42588      * @cfg {String} seperator    The value seperator normally ',' 
42589      */
42590     seperator : ',',
42591     
42592     // private the array of items that are displayed..
42593     items  : false,
42594     // private - the hidden field el.
42595     hiddenEl : false,
42596     // private - the filed el..
42597     el : false,
42598     
42599     //validateValue : function() { return true; }, // all values are ok!
42600     //onAddClick: function() { },
42601     
42602     onRender : function(ct, position) 
42603     {
42604         
42605         // create the standard hidden element
42606         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42607         
42608         
42609         // give fake names to child combo;
42610         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42611         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42612         
42613         this.combo = Roo.factory(this.combo, Roo.form);
42614         this.combo.onRender(ct, position);
42615         if (typeof(this.combo.width) != 'undefined') {
42616             this.combo.onResize(this.combo.width,0);
42617         }
42618         
42619         this.combo.initEvents();
42620         
42621         // assigned so form know we need to do this..
42622         this.store          = this.combo.store;
42623         this.valueField     = this.combo.valueField;
42624         this.displayField   = this.combo.displayField ;
42625         
42626         
42627         this.combo.wrap.addClass('x-cbarray-grp');
42628         
42629         var cbwrap = this.combo.wrap.createChild(
42630             {tag: 'div', cls: 'x-cbarray-cb'},
42631             this.combo.el.dom
42632         );
42633         
42634              
42635         this.hiddenEl = this.combo.wrap.createChild({
42636             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42637         });
42638         this.el = this.combo.wrap.createChild({
42639             tag: 'input',  type:'hidden' , name: this.name, value : ''
42640         });
42641          //   this.el.dom.removeAttribute("name");
42642         
42643         
42644         this.outerWrap = this.combo.wrap;
42645         this.wrap = cbwrap;
42646         
42647         this.outerWrap.setWidth(this.width);
42648         this.outerWrap.dom.removeChild(this.el.dom);
42649         
42650         this.wrap.dom.appendChild(this.el.dom);
42651         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42652         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42653         
42654         this.combo.trigger.setStyle('position','relative');
42655         this.combo.trigger.setStyle('left', '0px');
42656         this.combo.trigger.setStyle('top', '2px');
42657         
42658         this.combo.el.setStyle('vertical-align', 'text-bottom');
42659         
42660         //this.trigger.setStyle('vertical-align', 'top');
42661         
42662         // this should use the code from combo really... on('add' ....)
42663         if (this.adder) {
42664             
42665         
42666             this.adder = this.outerWrap.createChild(
42667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42668             var _t = this;
42669             this.adder.on('click', function(e) {
42670                 _t.fireEvent('adderclick', this, e);
42671             }, _t);
42672         }
42673         //var _t = this;
42674         //this.adder.on('click', this.onAddClick, _t);
42675         
42676         
42677         this.combo.on('select', function(cb, rec, ix) {
42678             this.addItem(rec.data);
42679             
42680             cb.setValue('');
42681             cb.el.dom.value = '';
42682             //cb.lastData = rec.data;
42683             // add to list
42684             
42685         }, this);
42686         
42687         
42688     },
42689     
42690     
42691     getName: function()
42692     {
42693         // returns hidden if it's set..
42694         if (!this.rendered) {return ''};
42695         return  this.hiddenName ? this.hiddenName : this.name;
42696         
42697     },
42698     
42699     
42700     onResize: function(w, h){
42701         
42702         return;
42703         // not sure if this is needed..
42704         //this.combo.onResize(w,h);
42705         
42706         if(typeof w != 'number'){
42707             // we do not handle it!?!?
42708             return;
42709         }
42710         var tw = this.combo.trigger.getWidth();
42711         tw += this.addicon ? this.addicon.getWidth() : 0;
42712         tw += this.editicon ? this.editicon.getWidth() : 0;
42713         var x = w - tw;
42714         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42715             
42716         this.combo.trigger.setStyle('left', '0px');
42717         
42718         if(this.list && this.listWidth === undefined){
42719             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42720             this.list.setWidth(lw);
42721             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42722         }
42723         
42724     
42725         
42726     },
42727     
42728     addItem: function(rec)
42729     {
42730         var valueField = this.combo.valueField;
42731         var displayField = this.combo.displayField;
42732         
42733         if (this.items.indexOfKey(rec[valueField]) > -1) {
42734             //console.log("GOT " + rec.data.id);
42735             return;
42736         }
42737         
42738         var x = new Roo.form.ComboBoxArray.Item({
42739             //id : rec[this.idField],
42740             data : rec,
42741             displayField : displayField ,
42742             tipField : displayField ,
42743             cb : this
42744         });
42745         // use the 
42746         this.items.add(rec[valueField],x);
42747         // add it before the element..
42748         this.updateHiddenEl();
42749         x.render(this.outerWrap, this.wrap.dom);
42750         // add the image handler..
42751     },
42752     
42753     updateHiddenEl : function()
42754     {
42755         this.validate();
42756         if (!this.hiddenEl) {
42757             return;
42758         }
42759         var ar = [];
42760         var idField = this.combo.valueField;
42761         
42762         this.items.each(function(f) {
42763             ar.push(f.data[idField]);
42764         });
42765         this.hiddenEl.dom.value = ar.join(this.seperator);
42766         this.validate();
42767     },
42768     
42769     reset : function()
42770     {
42771         this.items.clear();
42772         
42773         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42774            el.remove();
42775         });
42776         
42777         this.el.dom.value = '';
42778         if (this.hiddenEl) {
42779             this.hiddenEl.dom.value = '';
42780         }
42781         
42782     },
42783     getValue: function()
42784     {
42785         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42786     },
42787     setValue: function(v) // not a valid action - must use addItems..
42788     {
42789         
42790         this.reset();
42791          
42792         if (this.store.isLocal && (typeof(v) == 'string')) {
42793             // then we can use the store to find the values..
42794             // comma seperated at present.. this needs to allow JSON based encoding..
42795             this.hiddenEl.value  = v;
42796             var v_ar = [];
42797             Roo.each(v.split(this.seperator), function(k) {
42798                 Roo.log("CHECK " + this.valueField + ',' + k);
42799                 var li = this.store.query(this.valueField, k);
42800                 if (!li.length) {
42801                     return;
42802                 }
42803                 var add = {};
42804                 add[this.valueField] = k;
42805                 add[this.displayField] = li.item(0).data[this.displayField];
42806                 
42807                 this.addItem(add);
42808             }, this) 
42809              
42810         }
42811         if (typeof(v) == 'object' ) {
42812             // then let's assume it's an array of objects..
42813             Roo.each(v, function(l) {
42814                 var add = l;
42815                 if (typeof(l) == 'string') {
42816                     add = {};
42817                     add[this.valueField] = l;
42818                     add[this.displayField] = l
42819                 }
42820                 this.addItem(add);
42821             }, this);
42822              
42823         }
42824         
42825         
42826     },
42827     setFromData: function(v)
42828     {
42829         // this recieves an object, if setValues is called.
42830         this.reset();
42831         this.el.dom.value = v[this.displayField];
42832         this.hiddenEl.dom.value = v[this.valueField];
42833         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42834             return;
42835         }
42836         var kv = v[this.valueField];
42837         var dv = v[this.displayField];
42838         kv = typeof(kv) != 'string' ? '' : kv;
42839         dv = typeof(dv) != 'string' ? '' : dv;
42840         
42841         
42842         var keys = kv.split(this.seperator);
42843         var display = dv.split(this.seperator);
42844         for (var i = 0 ; i < keys.length; i++) {
42845             add = {};
42846             add[this.valueField] = keys[i];
42847             add[this.displayField] = display[i];
42848             this.addItem(add);
42849         }
42850       
42851         
42852     },
42853     
42854     /**
42855      * Validates the combox array value
42856      * @return {Boolean} True if the value is valid, else false
42857      */
42858     validate : function(){
42859         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42860             this.clearInvalid();
42861             return true;
42862         }
42863         return false;
42864     },
42865     
42866     validateValue : function(value){
42867         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42868         
42869     },
42870     
42871     /*@
42872      * overide
42873      * 
42874      */
42875     isDirty : function() {
42876         if(this.disabled) {
42877             return false;
42878         }
42879         
42880         try {
42881             var d = Roo.decode(String(this.originalValue));
42882         } catch (e) {
42883             return String(this.getValue()) !== String(this.originalValue);
42884         }
42885         
42886         var originalValue = [];
42887         
42888         for (var i = 0; i < d.length; i++){
42889             originalValue.push(d[i][this.valueField]);
42890         }
42891         
42892         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42893         
42894     }
42895     
42896 });
42897
42898
42899
42900 /**
42901  * @class Roo.form.ComboBoxArray.Item
42902  * @extends Roo.BoxComponent
42903  * A selected item in the list
42904  *  Fred [x]  Brian [x]  [Pick another |v]
42905  * 
42906  * @constructor
42907  * Create a new item.
42908  * @param {Object} config Configuration options
42909  */
42910  
42911 Roo.form.ComboBoxArray.Item = function(config) {
42912     config.id = Roo.id();
42913     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42914 }
42915
42916 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42917     data : {},
42918     cb: false,
42919     displayField : false,
42920     tipField : false,
42921     
42922     
42923     defaultAutoCreate : {
42924         tag: 'div',
42925         cls: 'x-cbarray-item',
42926         cn : [ 
42927             { tag: 'div' },
42928             {
42929                 tag: 'img',
42930                 width:16,
42931                 height : 16,
42932                 src : Roo.BLANK_IMAGE_URL ,
42933                 align: 'center'
42934             }
42935         ]
42936         
42937     },
42938     
42939  
42940     onRender : function(ct, position)
42941     {
42942         Roo.form.Field.superclass.onRender.call(this, ct, position);
42943         
42944         if(!this.el){
42945             var cfg = this.getAutoCreate();
42946             this.el = ct.createChild(cfg, position);
42947         }
42948         
42949         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42950         
42951         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42952             this.cb.renderer(this.data) :
42953             String.format('{0}',this.data[this.displayField]);
42954         
42955             
42956         this.el.child('div').dom.setAttribute('qtip',
42957                         String.format('{0}',this.data[this.tipField])
42958         );
42959         
42960         this.el.child('img').on('click', this.remove, this);
42961         
42962     },
42963    
42964     remove : function()
42965     {
42966         if(this.cb.disabled){
42967             return;
42968         }
42969         
42970         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42971             this.cb.items.remove(this);
42972             this.el.child('img').un('click', this.remove, this);
42973             this.el.remove();
42974             this.cb.updateHiddenEl();
42975
42976             this.cb.fireEvent('remove', this.cb, this);
42977         }
42978         
42979     }
42980 });/*
42981  * RooJS Library 1.1.1
42982  * Copyright(c) 2008-2011  Alan Knowles
42983  *
42984  * License - LGPL
42985  */
42986  
42987
42988 /**
42989  * @class Roo.form.ComboNested
42990  * @extends Roo.form.ComboBox
42991  * A combobox for that allows selection of nested items in a list,
42992  * eg.
42993  *
42994  *  Book
42995  *    -> red
42996  *    -> green
42997  *  Table
42998  *    -> square
42999  *      ->red
43000  *      ->green
43001  *    -> rectangle
43002  *      ->green
43003  *      
43004  * 
43005  * @constructor
43006  * Create a new ComboNested
43007  * @param {Object} config Configuration options
43008  */
43009 Roo.form.ComboNested = function(config){
43010     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43011     // should verify some data...
43012     // like
43013     // hiddenName = required..
43014     // displayField = required
43015     // valudField == required
43016     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43017     var _t = this;
43018     Roo.each(req, function(e) {
43019         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43020             throw "Roo.form.ComboNested : missing value for: " + e;
43021         }
43022     });
43023      
43024     
43025 };
43026
43027 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43028    
43029     /*
43030      * @config {Number} max Number of columns to show
43031      */
43032     
43033     maxColumns : 3,
43034    
43035     list : null, // the outermost div..
43036     innerLists : null, // the
43037     views : null,
43038     stores : null,
43039     // private
43040     loadingChildren : false,
43041     
43042     onRender : function(ct, position)
43043     {
43044         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43045         
43046         if(this.hiddenName){
43047             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43048                     'before', true);
43049             this.hiddenField.value =
43050                 this.hiddenValue !== undefined ? this.hiddenValue :
43051                 this.value !== undefined ? this.value : '';
43052
43053             // prevent input submission
43054             this.el.dom.removeAttribute('name');
43055              
43056              
43057         }
43058         
43059         if(Roo.isGecko){
43060             this.el.dom.setAttribute('autocomplete', 'off');
43061         }
43062
43063         var cls = 'x-combo-list';
43064
43065         this.list = new Roo.Layer({
43066             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43067         });
43068
43069         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43070         this.list.setWidth(lw);
43071         this.list.swallowEvent('mousewheel');
43072         this.assetHeight = 0;
43073
43074         if(this.title){
43075             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43076             this.assetHeight += this.header.getHeight();
43077         }
43078         this.innerLists = [];
43079         this.views = [];
43080         this.stores = [];
43081         for (var i =0 ; i < this.maxColumns; i++) {
43082             this.onRenderList( cls, i);
43083         }
43084         
43085         // always needs footer, as we are going to have an 'OK' button.
43086         this.footer = this.list.createChild({cls:cls+'-ft'});
43087         this.pageTb = new Roo.Toolbar(this.footer);  
43088         var _this = this;
43089         this.pageTb.add(  {
43090             
43091             text: 'Done',
43092             handler: function()
43093             {
43094                 _this.collapse();
43095             }
43096         });
43097         
43098         if ( this.allowBlank && !this.disableClear) {
43099             
43100             this.pageTb.add(new Roo.Toolbar.Fill(), {
43101                 cls: 'x-btn-icon x-btn-clear',
43102                 text: '&#160;',
43103                 handler: function()
43104                 {
43105                     _this.collapse();
43106                     _this.clearValue();
43107                     _this.onSelect(false, -1);
43108                 }
43109             });
43110         }
43111         if (this.footer) {
43112             this.assetHeight += this.footer.getHeight();
43113         }
43114         
43115     },
43116     onRenderList : function (  cls, i)
43117     {
43118         
43119         var lw = Math.floor(
43120                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43121         );
43122         
43123         this.list.setWidth(lw); // default to '1'
43124
43125         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43126         //il.on('mouseover', this.onViewOver, this, { list:  i });
43127         //il.on('mousemove', this.onViewMove, this, { list:  i });
43128         il.setWidth(lw);
43129         il.setStyle({ 'overflow-x' : 'hidden'});
43130
43131         if(!this.tpl){
43132             this.tpl = new Roo.Template({
43133                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43134                 isEmpty: function (value, allValues) {
43135                     //Roo.log(value);
43136                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43137                     return dl ? 'has-children' : 'no-children'
43138                 }
43139             });
43140         }
43141         
43142         var store  = this.store;
43143         if (i > 0) {
43144             store  = new Roo.data.SimpleStore({
43145                 //fields : this.store.reader.meta.fields,
43146                 reader : this.store.reader,
43147                 data : [ ]
43148             });
43149         }
43150         this.stores[i]  = store;
43151                   
43152         var view = this.views[i] = new Roo.View(
43153             il,
43154             this.tpl,
43155             {
43156                 singleSelect:true,
43157                 store: store,
43158                 selectedClass: this.selectedClass
43159             }
43160         );
43161         view.getEl().setWidth(lw);
43162         view.getEl().setStyle({
43163             position: i < 1 ? 'relative' : 'absolute',
43164             top: 0,
43165             left: (i * lw ) + 'px',
43166             display : i > 0 ? 'none' : 'block'
43167         });
43168         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43169         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43170         //view.on('click', this.onViewClick, this, { list : i });
43171
43172         store.on('beforeload', this.onBeforeLoad, this);
43173         store.on('load',  this.onLoad, this, { list  : i});
43174         store.on('loadexception', this.onLoadException, this);
43175
43176         // hide the other vies..
43177         
43178         
43179         
43180     },
43181       
43182     restrictHeight : function()
43183     {
43184         var mh = 0;
43185         Roo.each(this.innerLists, function(il,i) {
43186             var el = this.views[i].getEl();
43187             el.dom.style.height = '';
43188             var inner = el.dom;
43189             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43190             // only adjust heights on other ones..
43191             mh = Math.max(h, mh);
43192             if (i < 1) {
43193                 
43194                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43195                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43196                
43197             }
43198             
43199             
43200         }, this);
43201         
43202         this.list.beginUpdate();
43203         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43204         this.list.alignTo(this.el, this.listAlign);
43205         this.list.endUpdate();
43206         
43207     },
43208      
43209     
43210     // -- store handlers..
43211     // private
43212     onBeforeLoad : function()
43213     {
43214         if(!this.hasFocus){
43215             return;
43216         }
43217         this.innerLists[0].update(this.loadingText ?
43218                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43219         this.restrictHeight();
43220         this.selectedIndex = -1;
43221     },
43222     // private
43223     onLoad : function(a,b,c,d)
43224     {
43225         if (!this.loadingChildren) {
43226             // then we are loading the top level. - hide the children
43227             for (var i = 1;i < this.views.length; i++) {
43228                 this.views[i].getEl().setStyle({ display : 'none' });
43229             }
43230             var lw = Math.floor(
43231                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43232             );
43233         
43234              this.list.setWidth(lw); // default to '1'
43235
43236             
43237         }
43238         if(!this.hasFocus){
43239             return;
43240         }
43241         
43242         if(this.store.getCount() > 0) {
43243             this.expand();
43244             this.restrictHeight();   
43245         } else {
43246             this.onEmptyResults();
43247         }
43248         
43249         if (!this.loadingChildren) {
43250             this.selectActive();
43251         }
43252         /*
43253         this.stores[1].loadData([]);
43254         this.stores[2].loadData([]);
43255         this.views
43256         */    
43257     
43258         //this.el.focus();
43259     },
43260     
43261     
43262     // private
43263     onLoadException : function()
43264     {
43265         this.collapse();
43266         Roo.log(this.store.reader.jsonData);
43267         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43268             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43269         }
43270         
43271         
43272     },
43273     // no cleaning of leading spaces on blur here.
43274     cleanLeadingSpace : function(e) { },
43275     
43276
43277     onSelectChange : function (view, sels, opts )
43278     {
43279         var ix = view.getSelectedIndexes();
43280          
43281         if (opts.list > this.maxColumns - 2) {
43282             if (view.store.getCount()<  1) {
43283                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43284
43285             } else  {
43286                 if (ix.length) {
43287                     // used to clear ?? but if we are loading unselected 
43288                     this.setFromData(view.store.getAt(ix[0]).data);
43289                 }
43290                 
43291             }
43292             
43293             return;
43294         }
43295         
43296         if (!ix.length) {
43297             // this get's fired when trigger opens..
43298            // this.setFromData({});
43299             var str = this.stores[opts.list+1];
43300             str.data.clear(); // removeall wihtout the fire events..
43301             return;
43302         }
43303         
43304         var rec = view.store.getAt(ix[0]);
43305          
43306         this.setFromData(rec.data);
43307         this.fireEvent('select', this, rec, ix[0]);
43308         
43309         var lw = Math.floor(
43310              (
43311                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43312              ) / this.maxColumns
43313         );
43314         this.loadingChildren = true;
43315         this.stores[opts.list+1].loadDataFromChildren( rec );
43316         this.loadingChildren = false;
43317         var dl = this.stores[opts.list+1]. getTotalCount();
43318         
43319         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43320         
43321         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43322         for (var i = opts.list+2; i < this.views.length;i++) {
43323             this.views[i].getEl().setStyle({ display : 'none' });
43324         }
43325         
43326         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43327         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43328         
43329         if (this.isLoading) {
43330            // this.selectActive(opts.list);
43331         }
43332          
43333     },
43334     
43335     
43336     
43337     
43338     onDoubleClick : function()
43339     {
43340         this.collapse(); //??
43341     },
43342     
43343      
43344     
43345     
43346     
43347     // private
43348     recordToStack : function(store, prop, value, stack)
43349     {
43350         var cstore = new Roo.data.SimpleStore({
43351             //fields : this.store.reader.meta.fields, // we need array reader.. for
43352             reader : this.store.reader,
43353             data : [ ]
43354         });
43355         var _this = this;
43356         var record  = false;
43357         var srec = false;
43358         if(store.getCount() < 1){
43359             return false;
43360         }
43361         store.each(function(r){
43362             if(r.data[prop] == value){
43363                 record = r;
43364             srec = r;
43365                 return false;
43366             }
43367             if (r.data.cn && r.data.cn.length) {
43368                 cstore.loadDataFromChildren( r);
43369                 var cret = _this.recordToStack(cstore, prop, value, stack);
43370                 if (cret !== false) {
43371                     record = cret;
43372                     srec = r;
43373                     return false;
43374                 }
43375             }
43376              
43377             return true;
43378         });
43379         if (record == false) {
43380             return false
43381         }
43382         stack.unshift(srec);
43383         return record;
43384     },
43385     
43386     /*
43387      * find the stack of stores that match our value.
43388      *
43389      * 
43390      */
43391     
43392     selectActive : function ()
43393     {
43394         // if store is not loaded, then we will need to wait for that to happen first.
43395         var stack = [];
43396         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43397         for (var i = 0; i < stack.length; i++ ) {
43398             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43399         }
43400         
43401     }
43402         
43403          
43404     
43405     
43406     
43407     
43408 });/*
43409  * Based on:
43410  * Ext JS Library 1.1.1
43411  * Copyright(c) 2006-2007, Ext JS, LLC.
43412  *
43413  * Originally Released Under LGPL - original licence link has changed is not relivant.
43414  *
43415  * Fork - LGPL
43416  * <script type="text/javascript">
43417  */
43418 /**
43419  * @class Roo.form.Checkbox
43420  * @extends Roo.form.Field
43421  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43422  * @constructor
43423  * Creates a new Checkbox
43424  * @param {Object} config Configuration options
43425  */
43426 Roo.form.Checkbox = function(config){
43427     Roo.form.Checkbox.superclass.constructor.call(this, config);
43428     this.addEvents({
43429         /**
43430          * @event check
43431          * Fires when the checkbox is checked or unchecked.
43432              * @param {Roo.form.Checkbox} this This checkbox
43433              * @param {Boolean} checked The new checked value
43434              */
43435         check : true
43436     });
43437 };
43438
43439 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43440     /**
43441      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43442      */
43443     focusClass : undefined,
43444     /**
43445      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43446      */
43447     fieldClass: "x-form-field",
43448     /**
43449      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43450      */
43451     checked: false,
43452     /**
43453      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43454      * {tag: "input", type: "checkbox", autocomplete: "off"})
43455      */
43456     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43457     /**
43458      * @cfg {String} boxLabel The text that appears beside the checkbox
43459      */
43460     boxLabel : "",
43461     /**
43462      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43463      */  
43464     inputValue : '1',
43465     /**
43466      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43467      */
43468      valueOff: '0', // value when not checked..
43469
43470     actionMode : 'viewEl', 
43471     //
43472     // private
43473     itemCls : 'x-menu-check-item x-form-item',
43474     groupClass : 'x-menu-group-item',
43475     inputType : 'hidden',
43476     
43477     
43478     inSetChecked: false, // check that we are not calling self...
43479     
43480     inputElement: false, // real input element?
43481     basedOn: false, // ????
43482     
43483     isFormField: true, // not sure where this is needed!!!!
43484
43485     onResize : function(){
43486         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43487         if(!this.boxLabel){
43488             this.el.alignTo(this.wrap, 'c-c');
43489         }
43490     },
43491
43492     initEvents : function(){
43493         Roo.form.Checkbox.superclass.initEvents.call(this);
43494         this.el.on("click", this.onClick,  this);
43495         this.el.on("change", this.onClick,  this);
43496     },
43497
43498
43499     getResizeEl : function(){
43500         return this.wrap;
43501     },
43502
43503     getPositionEl : function(){
43504         return this.wrap;
43505     },
43506
43507     // private
43508     onRender : function(ct, position){
43509         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43510         /*
43511         if(this.inputValue !== undefined){
43512             this.el.dom.value = this.inputValue;
43513         }
43514         */
43515         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43516         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43517         var viewEl = this.wrap.createChild({ 
43518             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43519         this.viewEl = viewEl;   
43520         this.wrap.on('click', this.onClick,  this); 
43521         
43522         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43523         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43524         
43525         
43526         
43527         if(this.boxLabel){
43528             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43529         //    viewEl.on('click', this.onClick,  this); 
43530         }
43531         //if(this.checked){
43532             this.setChecked(this.checked);
43533         //}else{
43534             //this.checked = this.el.dom;
43535         //}
43536
43537     },
43538
43539     // private
43540     initValue : Roo.emptyFn,
43541
43542     /**
43543      * Returns the checked state of the checkbox.
43544      * @return {Boolean} True if checked, else false
43545      */
43546     getValue : function(){
43547         if(this.el){
43548             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43549         }
43550         return this.valueOff;
43551         
43552     },
43553
43554         // private
43555     onClick : function(){ 
43556         if (this.disabled) {
43557             return;
43558         }
43559         this.setChecked(!this.checked);
43560
43561         //if(this.el.dom.checked != this.checked){
43562         //    this.setValue(this.el.dom.checked);
43563        // }
43564     },
43565
43566     /**
43567      * Sets the checked state of the checkbox.
43568      * On is always based on a string comparison between inputValue and the param.
43569      * @param {Boolean/String} value - the value to set 
43570      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43571      */
43572     setValue : function(v,suppressEvent){
43573         
43574         
43575         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43576         //if(this.el && this.el.dom){
43577         //    this.el.dom.checked = this.checked;
43578         //    this.el.dom.defaultChecked = this.checked;
43579         //}
43580         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43581         //this.fireEvent("check", this, this.checked);
43582     },
43583     // private..
43584     setChecked : function(state,suppressEvent)
43585     {
43586         if (this.inSetChecked) {
43587             this.checked = state;
43588             return;
43589         }
43590         
43591     
43592         if(this.wrap){
43593             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43594         }
43595         this.checked = state;
43596         if(suppressEvent !== true){
43597             this.fireEvent('check', this, state);
43598         }
43599         this.inSetChecked = true;
43600         this.el.dom.value = state ? this.inputValue : this.valueOff;
43601         this.inSetChecked = false;
43602         
43603     },
43604     // handle setting of hidden value by some other method!!?!?
43605     setFromHidden: function()
43606     {
43607         if(!this.el){
43608             return;
43609         }
43610         //console.log("SET FROM HIDDEN");
43611         //alert('setFrom hidden');
43612         this.setValue(this.el.dom.value);
43613     },
43614     
43615     onDestroy : function()
43616     {
43617         if(this.viewEl){
43618             Roo.get(this.viewEl).remove();
43619         }
43620          
43621         Roo.form.Checkbox.superclass.onDestroy.call(this);
43622     },
43623     
43624     setBoxLabel : function(str)
43625     {
43626         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43627     }
43628
43629 });/*
43630  * Based on:
43631  * Ext JS Library 1.1.1
43632  * Copyright(c) 2006-2007, Ext JS, LLC.
43633  *
43634  * Originally Released Under LGPL - original licence link has changed is not relivant.
43635  *
43636  * Fork - LGPL
43637  * <script type="text/javascript">
43638  */
43639  
43640 /**
43641  * @class Roo.form.Radio
43642  * @extends Roo.form.Checkbox
43643  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43644  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43645  * @constructor
43646  * Creates a new Radio
43647  * @param {Object} config Configuration options
43648  */
43649 Roo.form.Radio = function(){
43650     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43651 };
43652 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43653     inputType: 'radio',
43654
43655     /**
43656      * If this radio is part of a group, it will return the selected value
43657      * @return {String}
43658      */
43659     getGroupValue : function(){
43660         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43661     },
43662     
43663     
43664     onRender : function(ct, position){
43665         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43666         
43667         if(this.inputValue !== undefined){
43668             this.el.dom.value = this.inputValue;
43669         }
43670          
43671         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43672         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43673         //var viewEl = this.wrap.createChild({ 
43674         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43675         //this.viewEl = viewEl;   
43676         //this.wrap.on('click', this.onClick,  this); 
43677         
43678         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43679         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43680         
43681         
43682         
43683         if(this.boxLabel){
43684             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43685         //    viewEl.on('click', this.onClick,  this); 
43686         }
43687          if(this.checked){
43688             this.el.dom.checked =   'checked' ;
43689         }
43690          
43691     } 
43692     
43693     
43694 });//<script type="text/javascript">
43695
43696 /*
43697  * Based  Ext JS Library 1.1.1
43698  * Copyright(c) 2006-2007, Ext JS, LLC.
43699  * LGPL
43700  *
43701  */
43702  
43703 /**
43704  * @class Roo.HtmlEditorCore
43705  * @extends Roo.Component
43706  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43707  *
43708  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43709  */
43710
43711 Roo.HtmlEditorCore = function(config){
43712     
43713     
43714     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43715     
43716     
43717     this.addEvents({
43718         /**
43719          * @event initialize
43720          * Fires when the editor is fully initialized (including the iframe)
43721          * @param {Roo.HtmlEditorCore} this
43722          */
43723         initialize: true,
43724         /**
43725          * @event activate
43726          * Fires when the editor is first receives the focus. Any insertion must wait
43727          * until after this event.
43728          * @param {Roo.HtmlEditorCore} this
43729          */
43730         activate: true,
43731          /**
43732          * @event beforesync
43733          * Fires before the textarea is updated with content from the editor iframe. Return false
43734          * to cancel the sync.
43735          * @param {Roo.HtmlEditorCore} this
43736          * @param {String} html
43737          */
43738         beforesync: true,
43739          /**
43740          * @event beforepush
43741          * Fires before the iframe editor is updated with content from the textarea. Return false
43742          * to cancel the push.
43743          * @param {Roo.HtmlEditorCore} this
43744          * @param {String} html
43745          */
43746         beforepush: true,
43747          /**
43748          * @event sync
43749          * Fires when the textarea is updated with content from the editor iframe.
43750          * @param {Roo.HtmlEditorCore} this
43751          * @param {String} html
43752          */
43753         sync: true,
43754          /**
43755          * @event push
43756          * Fires when the iframe editor is updated with content from the textarea.
43757          * @param {Roo.HtmlEditorCore} this
43758          * @param {String} html
43759          */
43760         push: true,
43761         
43762         /**
43763          * @event editorevent
43764          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43765          * @param {Roo.HtmlEditorCore} this
43766          */
43767         editorevent: true
43768         
43769     });
43770     
43771     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43772     
43773     // defaults : white / black...
43774     this.applyBlacklists();
43775     
43776     
43777     
43778 };
43779
43780
43781 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43782
43783
43784      /**
43785      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43786      */
43787     
43788     owner : false,
43789     
43790      /**
43791      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43792      *                        Roo.resizable.
43793      */
43794     resizable : false,
43795      /**
43796      * @cfg {Number} height (in pixels)
43797      */   
43798     height: 300,
43799    /**
43800      * @cfg {Number} width (in pixels)
43801      */   
43802     width: 500,
43803     
43804     /**
43805      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43806      * 
43807      */
43808     stylesheets: false,
43809     
43810     /**
43811      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
43812      */
43813     allowComments: false,
43814     // id of frame..
43815     frameId: false,
43816     
43817     // private properties
43818     validationEvent : false,
43819     deferHeight: true,
43820     initialized : false,
43821     activated : false,
43822     sourceEditMode : false,
43823     onFocus : Roo.emptyFn,
43824     iframePad:3,
43825     hideMode:'offsets',
43826     
43827     clearUp: true,
43828     
43829     // blacklist + whitelisted elements..
43830     black: false,
43831     white: false,
43832      
43833     bodyCls : '',
43834
43835     /**
43836      * Protected method that will not generally be called directly. It
43837      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43838      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43839      */
43840     getDocMarkup : function(){
43841         // body styles..
43842         var st = '';
43843         
43844         // inherit styels from page...?? 
43845         if (this.stylesheets === false) {
43846             
43847             Roo.get(document.head).select('style').each(function(node) {
43848                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43849             });
43850             
43851             Roo.get(document.head).select('link').each(function(node) { 
43852                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43853             });
43854             
43855         } else if (!this.stylesheets.length) {
43856                 // simple..
43857                 st = '<style type="text/css">' +
43858                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43859                    '</style>';
43860         } else {
43861             for (var i in this.stylesheets) { 
43862                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43863             }
43864             
43865         }
43866         
43867         st +=  '<style type="text/css">' +
43868             'IMG { cursor: pointer } ' +
43869         '</style>';
43870
43871         var cls = 'roo-htmleditor-body';
43872         
43873         if(this.bodyCls.length){
43874             cls += ' ' + this.bodyCls;
43875         }
43876         
43877         return '<html><head>' + st  +
43878             //<style type="text/css">' +
43879             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43880             //'</style>' +
43881             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43882     },
43883
43884     // private
43885     onRender : function(ct, position)
43886     {
43887         var _t = this;
43888         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43889         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43890         
43891         
43892         this.el.dom.style.border = '0 none';
43893         this.el.dom.setAttribute('tabIndex', -1);
43894         this.el.addClass('x-hidden hide');
43895         
43896         
43897         
43898         if(Roo.isIE){ // fix IE 1px bogus margin
43899             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43900         }
43901        
43902         
43903         this.frameId = Roo.id();
43904         
43905          
43906         
43907         var iframe = this.owner.wrap.createChild({
43908             tag: 'iframe',
43909             cls: 'form-control', // bootstrap..
43910             id: this.frameId,
43911             name: this.frameId,
43912             frameBorder : 'no',
43913             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43914         }, this.el
43915         );
43916         
43917         
43918         this.iframe = iframe.dom;
43919
43920          this.assignDocWin();
43921         
43922         this.doc.designMode = 'on';
43923        
43924         this.doc.open();
43925         this.doc.write(this.getDocMarkup());
43926         this.doc.close();
43927
43928         
43929         var task = { // must defer to wait for browser to be ready
43930             run : function(){
43931                 //console.log("run task?" + this.doc.readyState);
43932                 this.assignDocWin();
43933                 if(this.doc.body || this.doc.readyState == 'complete'){
43934                     try {
43935                         this.doc.designMode="on";
43936                     } catch (e) {
43937                         return;
43938                     }
43939                     Roo.TaskMgr.stop(task);
43940                     this.initEditor.defer(10, this);
43941                 }
43942             },
43943             interval : 10,
43944             duration: 10000,
43945             scope: this
43946         };
43947         Roo.TaskMgr.start(task);
43948
43949     },
43950
43951     // private
43952     onResize : function(w, h)
43953     {
43954          Roo.log('resize: ' +w + ',' + h );
43955         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43956         if(!this.iframe){
43957             return;
43958         }
43959         if(typeof w == 'number'){
43960             
43961             this.iframe.style.width = w + 'px';
43962         }
43963         if(typeof h == 'number'){
43964             
43965             this.iframe.style.height = h + 'px';
43966             if(this.doc){
43967                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43968             }
43969         }
43970         
43971     },
43972
43973     /**
43974      * Toggles the editor between standard and source edit mode.
43975      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43976      */
43977     toggleSourceEdit : function(sourceEditMode){
43978         
43979         this.sourceEditMode = sourceEditMode === true;
43980         
43981         if(this.sourceEditMode){
43982  
43983             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43984             
43985         }else{
43986             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43987             //this.iframe.className = '';
43988             this.deferFocus();
43989         }
43990         //this.setSize(this.owner.wrap.getSize());
43991         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43992     },
43993
43994     
43995   
43996
43997     /**
43998      * Protected method that will not generally be called directly. If you need/want
43999      * custom HTML cleanup, this is the method you should override.
44000      * @param {String} html The HTML to be cleaned
44001      * return {String} The cleaned HTML
44002      */
44003     cleanHtml : function(html){
44004         html = String(html);
44005         if(html.length > 5){
44006             if(Roo.isSafari){ // strip safari nonsense
44007                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44008             }
44009         }
44010         if(html == '&nbsp;'){
44011             html = '';
44012         }
44013         return html;
44014     },
44015
44016     /**
44017      * HTML Editor -> Textarea
44018      * Protected method that will not generally be called directly. Syncs the contents
44019      * of the editor iframe with the textarea.
44020      */
44021     syncValue : function(){
44022         if(this.initialized){
44023             var bd = (this.doc.body || this.doc.documentElement);
44024             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44025             var html = bd.innerHTML;
44026             if(Roo.isSafari){
44027                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44028                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44029                 if(m && m[1]){
44030                     html = '<div style="'+m[0]+'">' + html + '</div>';
44031                 }
44032             }
44033             html = this.cleanHtml(html);
44034             // fix up the special chars.. normaly like back quotes in word...
44035             // however we do not want to do this with chinese..
44036             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44037                 
44038                 var cc = match.charCodeAt();
44039
44040                 // Get the character value, handling surrogate pairs
44041                 if (match.length == 2) {
44042                     // It's a surrogate pair, calculate the Unicode code point
44043                     var high = match.charCodeAt(0) - 0xD800;
44044                     var low  = match.charCodeAt(1) - 0xDC00;
44045                     cc = (high * 0x400) + low + 0x10000;
44046                 }  else if (
44047                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44048                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44049                     (cc >= 0xf900 && cc < 0xfb00 )
44050                 ) {
44051                         return match;
44052                 }  
44053          
44054                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44055                 return "&#" + cc + ";";
44056                 
44057                 
44058             });
44059             
44060             
44061              
44062             if(this.owner.fireEvent('beforesync', this, html) !== false){
44063                 this.el.dom.value = html;
44064                 this.owner.fireEvent('sync', this, html);
44065             }
44066         }
44067     },
44068
44069     /**
44070      * Protected method that will not generally be called directly. Pushes the value of the textarea
44071      * into the iframe editor.
44072      */
44073     pushValue : function(){
44074         if(this.initialized){
44075             var v = this.el.dom.value.trim();
44076             
44077 //            if(v.length < 1){
44078 //                v = '&#160;';
44079 //            }
44080             
44081             if(this.owner.fireEvent('beforepush', this, v) !== false){
44082                 var d = (this.doc.body || this.doc.documentElement);
44083                 d.innerHTML = v;
44084                 this.cleanUpPaste();
44085                 this.el.dom.value = d.innerHTML;
44086                 this.owner.fireEvent('push', this, v);
44087             }
44088         }
44089     },
44090
44091     // private
44092     deferFocus : function(){
44093         this.focus.defer(10, this);
44094     },
44095
44096     // doc'ed in Field
44097     focus : function(){
44098         if(this.win && !this.sourceEditMode){
44099             this.win.focus();
44100         }else{
44101             this.el.focus();
44102         }
44103     },
44104     
44105     assignDocWin: function()
44106     {
44107         var iframe = this.iframe;
44108         
44109          if(Roo.isIE){
44110             this.doc = iframe.contentWindow.document;
44111             this.win = iframe.contentWindow;
44112         } else {
44113 //            if (!Roo.get(this.frameId)) {
44114 //                return;
44115 //            }
44116 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44117 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44118             
44119             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44120                 return;
44121             }
44122             
44123             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44124             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44125         }
44126     },
44127     
44128     // private
44129     initEditor : function(){
44130         //console.log("INIT EDITOR");
44131         this.assignDocWin();
44132         
44133         
44134         
44135         this.doc.designMode="on";
44136         this.doc.open();
44137         this.doc.write(this.getDocMarkup());
44138         this.doc.close();
44139         
44140         var dbody = (this.doc.body || this.doc.documentElement);
44141         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44142         // this copies styles from the containing element into thsi one..
44143         // not sure why we need all of this..
44144         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44145         
44146         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44147         //ss['background-attachment'] = 'fixed'; // w3c
44148         dbody.bgProperties = 'fixed'; // ie
44149         //Roo.DomHelper.applyStyles(dbody, ss);
44150         Roo.EventManager.on(this.doc, {
44151             //'mousedown': this.onEditorEvent,
44152             'mouseup': this.onEditorEvent,
44153             'dblclick': this.onEditorEvent,
44154             'click': this.onEditorEvent,
44155             'keyup': this.onEditorEvent,
44156             buffer:100,
44157             scope: this
44158         });
44159         if(Roo.isGecko){
44160             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44161         }
44162         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44163             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44164         }
44165         this.initialized = true;
44166
44167         this.owner.fireEvent('initialize', this);
44168         this.pushValue();
44169     },
44170
44171     // private
44172     onDestroy : function(){
44173         
44174         
44175         
44176         if(this.rendered){
44177             
44178             //for (var i =0; i < this.toolbars.length;i++) {
44179             //    // fixme - ask toolbars for heights?
44180             //    this.toolbars[i].onDestroy();
44181            // }
44182             
44183             //this.wrap.dom.innerHTML = '';
44184             //this.wrap.remove();
44185         }
44186     },
44187
44188     // private
44189     onFirstFocus : function(){
44190         
44191         this.assignDocWin();
44192         
44193         
44194         this.activated = true;
44195          
44196     
44197         if(Roo.isGecko){ // prevent silly gecko errors
44198             this.win.focus();
44199             var s = this.win.getSelection();
44200             if(!s.focusNode || s.focusNode.nodeType != 3){
44201                 var r = s.getRangeAt(0);
44202                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44203                 r.collapse(true);
44204                 this.deferFocus();
44205             }
44206             try{
44207                 this.execCmd('useCSS', true);
44208                 this.execCmd('styleWithCSS', false);
44209             }catch(e){}
44210         }
44211         this.owner.fireEvent('activate', this);
44212     },
44213
44214     // private
44215     adjustFont: function(btn){
44216         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44217         //if(Roo.isSafari){ // safari
44218         //    adjust *= 2;
44219        // }
44220         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44221         if(Roo.isSafari){ // safari
44222             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44223             v =  (v < 10) ? 10 : v;
44224             v =  (v > 48) ? 48 : v;
44225             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44226             
44227         }
44228         
44229         
44230         v = Math.max(1, v+adjust);
44231         
44232         this.execCmd('FontSize', v  );
44233     },
44234
44235     onEditorEvent : function(e)
44236     {
44237         this.owner.fireEvent('editorevent', this, e);
44238       //  this.updateToolbar();
44239         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44240     },
44241
44242     insertTag : function(tg)
44243     {
44244         // could be a bit smarter... -> wrap the current selected tRoo..
44245         if (tg.toLowerCase() == 'span' ||
44246             tg.toLowerCase() == 'code' ||
44247             tg.toLowerCase() == 'sup' ||
44248             tg.toLowerCase() == 'sub' 
44249             ) {
44250             
44251             range = this.createRange(this.getSelection());
44252             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44253             wrappingNode.appendChild(range.extractContents());
44254             range.insertNode(wrappingNode);
44255
44256             return;
44257             
44258             
44259             
44260         }
44261         this.execCmd("formatblock",   tg);
44262         
44263     },
44264     
44265     insertText : function(txt)
44266     {
44267         
44268         
44269         var range = this.createRange();
44270         range.deleteContents();
44271                //alert(Sender.getAttribute('label'));
44272                
44273         range.insertNode(this.doc.createTextNode(txt));
44274     } ,
44275     
44276      
44277
44278     /**
44279      * Executes a Midas editor command on the editor document and performs necessary focus and
44280      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44281      * @param {String} cmd The Midas command
44282      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44283      */
44284     relayCmd : function(cmd, value){
44285         this.win.focus();
44286         this.execCmd(cmd, value);
44287         this.owner.fireEvent('editorevent', this);
44288         //this.updateToolbar();
44289         this.owner.deferFocus();
44290     },
44291
44292     /**
44293      * Executes a Midas editor command directly on the editor document.
44294      * For visual commands, you should use {@link #relayCmd} instead.
44295      * <b>This should only be called after the editor is initialized.</b>
44296      * @param {String} cmd The Midas command
44297      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44298      */
44299     execCmd : function(cmd, value){
44300         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44301         this.syncValue();
44302     },
44303  
44304  
44305    
44306     /**
44307      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44308      * to insert tRoo.
44309      * @param {String} text | dom node.. 
44310      */
44311     insertAtCursor : function(text)
44312     {
44313         
44314         if(!this.activated){
44315             return;
44316         }
44317         /*
44318         if(Roo.isIE){
44319             this.win.focus();
44320             var r = this.doc.selection.createRange();
44321             if(r){
44322                 r.collapse(true);
44323                 r.pasteHTML(text);
44324                 this.syncValue();
44325                 this.deferFocus();
44326             
44327             }
44328             return;
44329         }
44330         */
44331         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44332             this.win.focus();
44333             
44334             
44335             // from jquery ui (MIT licenced)
44336             var range, node;
44337             var win = this.win;
44338             
44339             if (win.getSelection && win.getSelection().getRangeAt) {
44340                 range = win.getSelection().getRangeAt(0);
44341                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44342                 range.insertNode(node);
44343             } else if (win.document.selection && win.document.selection.createRange) {
44344                 // no firefox support
44345                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44346                 win.document.selection.createRange().pasteHTML(txt);
44347             } else {
44348                 // no firefox support
44349                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44350                 this.execCmd('InsertHTML', txt);
44351             } 
44352             
44353             this.syncValue();
44354             
44355             this.deferFocus();
44356         }
44357     },
44358  // private
44359     mozKeyPress : function(e){
44360         if(e.ctrlKey){
44361             var c = e.getCharCode(), cmd;
44362           
44363             if(c > 0){
44364                 c = String.fromCharCode(c).toLowerCase();
44365                 switch(c){
44366                     case 'b':
44367                         cmd = 'bold';
44368                         break;
44369                     case 'i':
44370                         cmd = 'italic';
44371                         break;
44372                     
44373                     case 'u':
44374                         cmd = 'underline';
44375                         break;
44376                     
44377                     case 'v':
44378                         this.cleanUpPaste.defer(100, this);
44379                         return;
44380                         
44381                 }
44382                 if(cmd){
44383                     this.win.focus();
44384                     this.execCmd(cmd);
44385                     this.deferFocus();
44386                     e.preventDefault();
44387                 }
44388                 
44389             }
44390         }
44391     },
44392
44393     // private
44394     fixKeys : function(){ // load time branching for fastest keydown performance
44395         if(Roo.isIE){
44396             return function(e){
44397                 var k = e.getKey(), r;
44398                 if(k == e.TAB){
44399                     e.stopEvent();
44400                     r = this.doc.selection.createRange();
44401                     if(r){
44402                         r.collapse(true);
44403                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44404                         this.deferFocus();
44405                     }
44406                     return;
44407                 }
44408                 
44409                 if(k == e.ENTER){
44410                     r = this.doc.selection.createRange();
44411                     if(r){
44412                         var target = r.parentElement();
44413                         if(!target || target.tagName.toLowerCase() != 'li'){
44414                             e.stopEvent();
44415                             r.pasteHTML('<br />');
44416                             r.collapse(false);
44417                             r.select();
44418                         }
44419                     }
44420                 }
44421                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44422                     this.cleanUpPaste.defer(100, this);
44423                     return;
44424                 }
44425                 
44426                 
44427             };
44428         }else if(Roo.isOpera){
44429             return function(e){
44430                 var k = e.getKey();
44431                 if(k == e.TAB){
44432                     e.stopEvent();
44433                     this.win.focus();
44434                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44435                     this.deferFocus();
44436                 }
44437                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44438                     this.cleanUpPaste.defer(100, this);
44439                     return;
44440                 }
44441                 
44442             };
44443         }else if(Roo.isSafari){
44444             return function(e){
44445                 var k = e.getKey();
44446                 
44447                 if(k == e.TAB){
44448                     e.stopEvent();
44449                     this.execCmd('InsertText','\t');
44450                     this.deferFocus();
44451                     return;
44452                 }
44453                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44454                     this.cleanUpPaste.defer(100, this);
44455                     return;
44456                 }
44457                 
44458              };
44459         }
44460     }(),
44461     
44462     getAllAncestors: function()
44463     {
44464         var p = this.getSelectedNode();
44465         var a = [];
44466         if (!p) {
44467             a.push(p); // push blank onto stack..
44468             p = this.getParentElement();
44469         }
44470         
44471         
44472         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44473             a.push(p);
44474             p = p.parentNode;
44475         }
44476         a.push(this.doc.body);
44477         return a;
44478     },
44479     lastSel : false,
44480     lastSelNode : false,
44481     
44482     
44483     getSelection : function() 
44484     {
44485         this.assignDocWin();
44486         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44487     },
44488     
44489     getSelectedNode: function() 
44490     {
44491         // this may only work on Gecko!!!
44492         
44493         // should we cache this!!!!
44494         
44495         
44496         
44497          
44498         var range = this.createRange(this.getSelection()).cloneRange();
44499         
44500         if (Roo.isIE) {
44501             var parent = range.parentElement();
44502             while (true) {
44503                 var testRange = range.duplicate();
44504                 testRange.moveToElementText(parent);
44505                 if (testRange.inRange(range)) {
44506                     break;
44507                 }
44508                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44509                     break;
44510                 }
44511                 parent = parent.parentElement;
44512             }
44513             return parent;
44514         }
44515         
44516         // is ancestor a text element.
44517         var ac =  range.commonAncestorContainer;
44518         if (ac.nodeType == 3) {
44519             ac = ac.parentNode;
44520         }
44521         
44522         var ar = ac.childNodes;
44523          
44524         var nodes = [];
44525         var other_nodes = [];
44526         var has_other_nodes = false;
44527         for (var i=0;i<ar.length;i++) {
44528             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44529                 continue;
44530             }
44531             // fullly contained node.
44532             
44533             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44534                 nodes.push(ar[i]);
44535                 continue;
44536             }
44537             
44538             // probably selected..
44539             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44540                 other_nodes.push(ar[i]);
44541                 continue;
44542             }
44543             // outer..
44544             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44545                 continue;
44546             }
44547             
44548             
44549             has_other_nodes = true;
44550         }
44551         if (!nodes.length && other_nodes.length) {
44552             nodes= other_nodes;
44553         }
44554         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44555             return false;
44556         }
44557         
44558         return nodes[0];
44559     },
44560     createRange: function(sel)
44561     {
44562         // this has strange effects when using with 
44563         // top toolbar - not sure if it's a great idea.
44564         //this.editor.contentWindow.focus();
44565         if (typeof sel != "undefined") {
44566             try {
44567                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44568             } catch(e) {
44569                 return this.doc.createRange();
44570             }
44571         } else {
44572             return this.doc.createRange();
44573         }
44574     },
44575     getParentElement: function()
44576     {
44577         
44578         this.assignDocWin();
44579         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44580         
44581         var range = this.createRange(sel);
44582          
44583         try {
44584             var p = range.commonAncestorContainer;
44585             while (p.nodeType == 3) { // text node
44586                 p = p.parentNode;
44587             }
44588             return p;
44589         } catch (e) {
44590             return null;
44591         }
44592     
44593     },
44594     /***
44595      *
44596      * Range intersection.. the hard stuff...
44597      *  '-1' = before
44598      *  '0' = hits..
44599      *  '1' = after.
44600      *         [ -- selected range --- ]
44601      *   [fail]                        [fail]
44602      *
44603      *    basically..
44604      *      if end is before start or  hits it. fail.
44605      *      if start is after end or hits it fail.
44606      *
44607      *   if either hits (but other is outside. - then it's not 
44608      *   
44609      *    
44610      **/
44611     
44612     
44613     // @see http://www.thismuchiknow.co.uk/?p=64.
44614     rangeIntersectsNode : function(range, node)
44615     {
44616         var nodeRange = node.ownerDocument.createRange();
44617         try {
44618             nodeRange.selectNode(node);
44619         } catch (e) {
44620             nodeRange.selectNodeContents(node);
44621         }
44622     
44623         var rangeStartRange = range.cloneRange();
44624         rangeStartRange.collapse(true);
44625     
44626         var rangeEndRange = range.cloneRange();
44627         rangeEndRange.collapse(false);
44628     
44629         var nodeStartRange = nodeRange.cloneRange();
44630         nodeStartRange.collapse(true);
44631     
44632         var nodeEndRange = nodeRange.cloneRange();
44633         nodeEndRange.collapse(false);
44634     
44635         return rangeStartRange.compareBoundaryPoints(
44636                  Range.START_TO_START, nodeEndRange) == -1 &&
44637                rangeEndRange.compareBoundaryPoints(
44638                  Range.START_TO_START, nodeStartRange) == 1;
44639         
44640          
44641     },
44642     rangeCompareNode : function(range, node)
44643     {
44644         var nodeRange = node.ownerDocument.createRange();
44645         try {
44646             nodeRange.selectNode(node);
44647         } catch (e) {
44648             nodeRange.selectNodeContents(node);
44649         }
44650         
44651         
44652         range.collapse(true);
44653     
44654         nodeRange.collapse(true);
44655      
44656         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44657         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44658          
44659         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44660         
44661         var nodeIsBefore   =  ss == 1;
44662         var nodeIsAfter    = ee == -1;
44663         
44664         if (nodeIsBefore && nodeIsAfter) {
44665             return 0; // outer
44666         }
44667         if (!nodeIsBefore && nodeIsAfter) {
44668             return 1; //right trailed.
44669         }
44670         
44671         if (nodeIsBefore && !nodeIsAfter) {
44672             return 2;  // left trailed.
44673         }
44674         // fully contined.
44675         return 3;
44676     },
44677
44678     // private? - in a new class?
44679     cleanUpPaste :  function()
44680     {
44681         // cleans up the whole document..
44682         Roo.log('cleanuppaste');
44683         
44684         this.cleanUpChildren(this.doc.body);
44685         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44686         if (clean != this.doc.body.innerHTML) {
44687             this.doc.body.innerHTML = clean;
44688         }
44689         
44690     },
44691     
44692     cleanWordChars : function(input) {// change the chars to hex code
44693         var he = Roo.HtmlEditorCore;
44694         
44695         var output = input;
44696         Roo.each(he.swapCodes, function(sw) { 
44697             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44698             
44699             output = output.replace(swapper, sw[1]);
44700         });
44701         
44702         return output;
44703     },
44704     
44705     
44706     cleanUpChildren : function (n)
44707     {
44708         if (!n.childNodes.length) {
44709             return;
44710         }
44711         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44712            this.cleanUpChild(n.childNodes[i]);
44713         }
44714     },
44715     
44716     
44717         
44718     
44719     cleanUpChild : function (node)
44720     {
44721         var ed = this;
44722         //console.log(node);
44723         if (node.nodeName == "#text") {
44724             // clean up silly Windows -- stuff?
44725             return; 
44726         }
44727         if (node.nodeName == "#comment") {
44728             if (!this.allowComments) {
44729                 node.parentNode.removeChild(node);
44730             }
44731             // clean up silly Windows -- stuff?
44732             return; 
44733         }
44734         var lcname = node.tagName.toLowerCase();
44735         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44736         // whitelist of tags..
44737         
44738         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44739             // remove node.
44740             node.parentNode.removeChild(node);
44741             return;
44742             
44743         }
44744         
44745         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44746         
44747         // spans with no attributes - just remove them..
44748         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44749             remove_keep_children = true;
44750         }
44751         
44752         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44753         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44754         
44755         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44756         //    remove_keep_children = true;
44757         //}
44758         
44759         if (remove_keep_children) {
44760             this.cleanUpChildren(node);
44761             // inserts everything just before this node...
44762             while (node.childNodes.length) {
44763                 var cn = node.childNodes[0];
44764                 node.removeChild(cn);
44765                 node.parentNode.insertBefore(cn, node);
44766             }
44767             node.parentNode.removeChild(node);
44768             return;
44769         }
44770         
44771         if (!node.attributes || !node.attributes.length) {
44772             
44773           
44774             
44775             
44776             this.cleanUpChildren(node);
44777             return;
44778         }
44779         
44780         function cleanAttr(n,v)
44781         {
44782             
44783             if (v.match(/^\./) || v.match(/^\//)) {
44784                 return;
44785             }
44786             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44787                 return;
44788             }
44789             if (v.match(/^#/)) {
44790                 return;
44791             }
44792             if (v.match(/^\{/)) { // allow template editing.
44793                 return;
44794             }
44795 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44796             node.removeAttribute(n);
44797             
44798         }
44799         
44800         var cwhite = this.cwhite;
44801         var cblack = this.cblack;
44802             
44803         function cleanStyle(n,v)
44804         {
44805             if (v.match(/expression/)) { //XSS?? should we even bother..
44806                 node.removeAttribute(n);
44807                 return;
44808             }
44809             
44810             var parts = v.split(/;/);
44811             var clean = [];
44812             
44813             Roo.each(parts, function(p) {
44814                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44815                 if (!p.length) {
44816                     return true;
44817                 }
44818                 var l = p.split(':').shift().replace(/\s+/g,'');
44819                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44820                 
44821                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44822 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44823                     //node.removeAttribute(n);
44824                     return true;
44825                 }
44826                 //Roo.log()
44827                 // only allow 'c whitelisted system attributes'
44828                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44829 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44830                     //node.removeAttribute(n);
44831                     return true;
44832                 }
44833                 
44834                 
44835                  
44836                 
44837                 clean.push(p);
44838                 return true;
44839             });
44840             if (clean.length) { 
44841                 node.setAttribute(n, clean.join(';'));
44842             } else {
44843                 node.removeAttribute(n);
44844             }
44845             
44846         }
44847         
44848         
44849         for (var i = node.attributes.length-1; i > -1 ; i--) {
44850             var a = node.attributes[i];
44851             //console.log(a);
44852             
44853             if (a.name.toLowerCase().substr(0,2)=='on')  {
44854                 node.removeAttribute(a.name);
44855                 continue;
44856             }
44857             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44858                 node.removeAttribute(a.name);
44859                 continue;
44860             }
44861             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44862                 cleanAttr(a.name,a.value); // fixme..
44863                 continue;
44864             }
44865             if (a.name == 'style') {
44866                 cleanStyle(a.name,a.value);
44867                 continue;
44868             }
44869             /// clean up MS crap..
44870             // tecnically this should be a list of valid class'es..
44871             
44872             
44873             if (a.name == 'class') {
44874                 if (a.value.match(/^Mso/)) {
44875                     node.removeAttribute('class');
44876                 }
44877                 
44878                 if (a.value.match(/^body$/)) {
44879                     node.removeAttribute('class');
44880                 }
44881                 continue;
44882             }
44883             
44884             // style cleanup!?
44885             // class cleanup?
44886             
44887         }
44888         
44889         
44890         this.cleanUpChildren(node);
44891         
44892         
44893     },
44894     
44895     /**
44896      * Clean up MS wordisms...
44897      */
44898     cleanWord : function(node)
44899     {
44900         if (!node) {
44901             this.cleanWord(this.doc.body);
44902             return;
44903         }
44904         
44905         if(
44906                 node.nodeName == 'SPAN' &&
44907                 !node.hasAttributes() &&
44908                 node.childNodes.length == 1 &&
44909                 node.firstChild.nodeName == "#text"  
44910         ) {
44911             var textNode = node.firstChild;
44912             node.removeChild(textNode);
44913             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44914                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44915             }
44916             node.parentNode.insertBefore(textNode, node);
44917             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44918                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44919             }
44920             node.parentNode.removeChild(node);
44921         }
44922         
44923         if (node.nodeName == "#text") {
44924             // clean up silly Windows -- stuff?
44925             return; 
44926         }
44927         if (node.nodeName == "#comment") {
44928             node.parentNode.removeChild(node);
44929             // clean up silly Windows -- stuff?
44930             return; 
44931         }
44932         
44933         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44934             node.parentNode.removeChild(node);
44935             return;
44936         }
44937         //Roo.log(node.tagName);
44938         // remove - but keep children..
44939         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44940             //Roo.log('-- removed');
44941             while (node.childNodes.length) {
44942                 var cn = node.childNodes[0];
44943                 node.removeChild(cn);
44944                 node.parentNode.insertBefore(cn, node);
44945                 // move node to parent - and clean it..
44946                 this.cleanWord(cn);
44947             }
44948             node.parentNode.removeChild(node);
44949             /// no need to iterate chidlren = it's got none..
44950             //this.iterateChildren(node, this.cleanWord);
44951             return;
44952         }
44953         // clean styles
44954         if (node.className.length) {
44955             
44956             var cn = node.className.split(/\W+/);
44957             var cna = [];
44958             Roo.each(cn, function(cls) {
44959                 if (cls.match(/Mso[a-zA-Z]+/)) {
44960                     return;
44961                 }
44962                 cna.push(cls);
44963             });
44964             node.className = cna.length ? cna.join(' ') : '';
44965             if (!cna.length) {
44966                 node.removeAttribute("class");
44967             }
44968         }
44969         
44970         if (node.hasAttribute("lang")) {
44971             node.removeAttribute("lang");
44972         }
44973         
44974         if (node.hasAttribute("style")) {
44975             
44976             var styles = node.getAttribute("style").split(";");
44977             var nstyle = [];
44978             Roo.each(styles, function(s) {
44979                 if (!s.match(/:/)) {
44980                     return;
44981                 }
44982                 var kv = s.split(":");
44983                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44984                     return;
44985                 }
44986                 // what ever is left... we allow.
44987                 nstyle.push(s);
44988             });
44989             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44990             if (!nstyle.length) {
44991                 node.removeAttribute('style');
44992             }
44993         }
44994         this.iterateChildren(node, this.cleanWord);
44995         
44996         
44997         
44998     },
44999     /**
45000      * iterateChildren of a Node, calling fn each time, using this as the scole..
45001      * @param {DomNode} node node to iterate children of.
45002      * @param {Function} fn method of this class to call on each item.
45003      */
45004     iterateChildren : function(node, fn)
45005     {
45006         if (!node.childNodes.length) {
45007                 return;
45008         }
45009         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45010            fn.call(this, node.childNodes[i])
45011         }
45012     },
45013     
45014     
45015     /**
45016      * cleanTableWidths.
45017      *
45018      * Quite often pasting from word etc.. results in tables with column and widths.
45019      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45020      *
45021      */
45022     cleanTableWidths : function(node)
45023     {
45024          
45025          
45026         if (!node) {
45027             this.cleanTableWidths(this.doc.body);
45028             return;
45029         }
45030         
45031         // ignore list...
45032         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45033             return; 
45034         }
45035         Roo.log(node.tagName);
45036         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45037             this.iterateChildren(node, this.cleanTableWidths);
45038             return;
45039         }
45040         if (node.hasAttribute('width')) {
45041             node.removeAttribute('width');
45042         }
45043         
45044          
45045         if (node.hasAttribute("style")) {
45046             // pretty basic...
45047             
45048             var styles = node.getAttribute("style").split(";");
45049             var nstyle = [];
45050             Roo.each(styles, function(s) {
45051                 if (!s.match(/:/)) {
45052                     return;
45053                 }
45054                 var kv = s.split(":");
45055                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45056                     return;
45057                 }
45058                 // what ever is left... we allow.
45059                 nstyle.push(s);
45060             });
45061             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45062             if (!nstyle.length) {
45063                 node.removeAttribute('style');
45064             }
45065         }
45066         
45067         this.iterateChildren(node, this.cleanTableWidths);
45068         
45069         
45070     },
45071     
45072     
45073     
45074     
45075     domToHTML : function(currentElement, depth, nopadtext) {
45076         
45077         depth = depth || 0;
45078         nopadtext = nopadtext || false;
45079     
45080         if (!currentElement) {
45081             return this.domToHTML(this.doc.body);
45082         }
45083         
45084         //Roo.log(currentElement);
45085         var j;
45086         var allText = false;
45087         var nodeName = currentElement.nodeName;
45088         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45089         
45090         if  (nodeName == '#text') {
45091             
45092             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45093         }
45094         
45095         
45096         var ret = '';
45097         if (nodeName != 'BODY') {
45098              
45099             var i = 0;
45100             // Prints the node tagName, such as <A>, <IMG>, etc
45101             if (tagName) {
45102                 var attr = [];
45103                 for(i = 0; i < currentElement.attributes.length;i++) {
45104                     // quoting?
45105                     var aname = currentElement.attributes.item(i).name;
45106                     if (!currentElement.attributes.item(i).value.length) {
45107                         continue;
45108                     }
45109                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45110                 }
45111                 
45112                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45113             } 
45114             else {
45115                 
45116                 // eack
45117             }
45118         } else {
45119             tagName = false;
45120         }
45121         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45122             return ret;
45123         }
45124         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45125             nopadtext = true;
45126         }
45127         
45128         
45129         // Traverse the tree
45130         i = 0;
45131         var currentElementChild = currentElement.childNodes.item(i);
45132         var allText = true;
45133         var innerHTML  = '';
45134         lastnode = '';
45135         while (currentElementChild) {
45136             // Formatting code (indent the tree so it looks nice on the screen)
45137             var nopad = nopadtext;
45138             if (lastnode == 'SPAN') {
45139                 nopad  = true;
45140             }
45141             // text
45142             if  (currentElementChild.nodeName == '#text') {
45143                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45144                 toadd = nopadtext ? toadd : toadd.trim();
45145                 if (!nopad && toadd.length > 80) {
45146                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45147                 }
45148                 innerHTML  += toadd;
45149                 
45150                 i++;
45151                 currentElementChild = currentElement.childNodes.item(i);
45152                 lastNode = '';
45153                 continue;
45154             }
45155             allText = false;
45156             
45157             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45158                 
45159             // Recursively traverse the tree structure of the child node
45160             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45161             lastnode = currentElementChild.nodeName;
45162             i++;
45163             currentElementChild=currentElement.childNodes.item(i);
45164         }
45165         
45166         ret += innerHTML;
45167         
45168         if (!allText) {
45169                 // The remaining code is mostly for formatting the tree
45170             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45171         }
45172         
45173         
45174         if (tagName) {
45175             ret+= "</"+tagName+">";
45176         }
45177         return ret;
45178         
45179     },
45180         
45181     applyBlacklists : function()
45182     {
45183         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45184         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45185         
45186         this.white = [];
45187         this.black = [];
45188         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45189             if (b.indexOf(tag) > -1) {
45190                 return;
45191             }
45192             this.white.push(tag);
45193             
45194         }, this);
45195         
45196         Roo.each(w, function(tag) {
45197             if (b.indexOf(tag) > -1) {
45198                 return;
45199             }
45200             if (this.white.indexOf(tag) > -1) {
45201                 return;
45202             }
45203             this.white.push(tag);
45204             
45205         }, this);
45206         
45207         
45208         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45209             if (w.indexOf(tag) > -1) {
45210                 return;
45211             }
45212             this.black.push(tag);
45213             
45214         }, this);
45215         
45216         Roo.each(b, function(tag) {
45217             if (w.indexOf(tag) > -1) {
45218                 return;
45219             }
45220             if (this.black.indexOf(tag) > -1) {
45221                 return;
45222             }
45223             this.black.push(tag);
45224             
45225         }, this);
45226         
45227         
45228         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45229         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45230         
45231         this.cwhite = [];
45232         this.cblack = [];
45233         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45234             if (b.indexOf(tag) > -1) {
45235                 return;
45236             }
45237             this.cwhite.push(tag);
45238             
45239         }, this);
45240         
45241         Roo.each(w, function(tag) {
45242             if (b.indexOf(tag) > -1) {
45243                 return;
45244             }
45245             if (this.cwhite.indexOf(tag) > -1) {
45246                 return;
45247             }
45248             this.cwhite.push(tag);
45249             
45250         }, this);
45251         
45252         
45253         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45254             if (w.indexOf(tag) > -1) {
45255                 return;
45256             }
45257             this.cblack.push(tag);
45258             
45259         }, this);
45260         
45261         Roo.each(b, function(tag) {
45262             if (w.indexOf(tag) > -1) {
45263                 return;
45264             }
45265             if (this.cblack.indexOf(tag) > -1) {
45266                 return;
45267             }
45268             this.cblack.push(tag);
45269             
45270         }, this);
45271     },
45272     
45273     setStylesheets : function(stylesheets)
45274     {
45275         if(typeof(stylesheets) == 'string'){
45276             Roo.get(this.iframe.contentDocument.head).createChild({
45277                 tag : 'link',
45278                 rel : 'stylesheet',
45279                 type : 'text/css',
45280                 href : stylesheets
45281             });
45282             
45283             return;
45284         }
45285         var _this = this;
45286      
45287         Roo.each(stylesheets, function(s) {
45288             if(!s.length){
45289                 return;
45290             }
45291             
45292             Roo.get(_this.iframe.contentDocument.head).createChild({
45293                 tag : 'link',
45294                 rel : 'stylesheet',
45295                 type : 'text/css',
45296                 href : s
45297             });
45298         });
45299
45300         
45301     },
45302     
45303     removeStylesheets : function()
45304     {
45305         var _this = this;
45306         
45307         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45308             s.remove();
45309         });
45310     },
45311     
45312     setStyle : function(style)
45313     {
45314         Roo.get(this.iframe.contentDocument.head).createChild({
45315             tag : 'style',
45316             type : 'text/css',
45317             html : style
45318         });
45319
45320         return;
45321     }
45322     
45323     // hide stuff that is not compatible
45324     /**
45325      * @event blur
45326      * @hide
45327      */
45328     /**
45329      * @event change
45330      * @hide
45331      */
45332     /**
45333      * @event focus
45334      * @hide
45335      */
45336     /**
45337      * @event specialkey
45338      * @hide
45339      */
45340     /**
45341      * @cfg {String} fieldClass @hide
45342      */
45343     /**
45344      * @cfg {String} focusClass @hide
45345      */
45346     /**
45347      * @cfg {String} autoCreate @hide
45348      */
45349     /**
45350      * @cfg {String} inputType @hide
45351      */
45352     /**
45353      * @cfg {String} invalidClass @hide
45354      */
45355     /**
45356      * @cfg {String} invalidText @hide
45357      */
45358     /**
45359      * @cfg {String} msgFx @hide
45360      */
45361     /**
45362      * @cfg {String} validateOnBlur @hide
45363      */
45364 });
45365
45366 Roo.HtmlEditorCore.white = [
45367         'area', 'br', 'img', 'input', 'hr', 'wbr',
45368         
45369        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45370        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45371        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45372        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45373        'table',   'ul',         'xmp', 
45374        
45375        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45376       'thead',   'tr', 
45377      
45378       'dir', 'menu', 'ol', 'ul', 'dl',
45379        
45380       'embed',  'object'
45381 ];
45382
45383
45384 Roo.HtmlEditorCore.black = [
45385     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45386         'applet', // 
45387         'base',   'basefont', 'bgsound', 'blink',  'body', 
45388         'frame',  'frameset', 'head',    'html',   'ilayer', 
45389         'iframe', 'layer',  'link',     'meta',    'object',   
45390         'script', 'style' ,'title',  'xml' // clean later..
45391 ];
45392 Roo.HtmlEditorCore.clean = [
45393     'script', 'style', 'title', 'xml'
45394 ];
45395 Roo.HtmlEditorCore.remove = [
45396     'font'
45397 ];
45398 // attributes..
45399
45400 Roo.HtmlEditorCore.ablack = [
45401     'on'
45402 ];
45403     
45404 Roo.HtmlEditorCore.aclean = [ 
45405     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45406 ];
45407
45408 // protocols..
45409 Roo.HtmlEditorCore.pwhite= [
45410         'http',  'https',  'mailto'
45411 ];
45412
45413 // white listed style attributes.
45414 Roo.HtmlEditorCore.cwhite= [
45415       //  'text-align', /// default is to allow most things..
45416       
45417          
45418 //        'font-size'//??
45419 ];
45420
45421 // black listed style attributes.
45422 Roo.HtmlEditorCore.cblack= [
45423       //  'font-size' -- this can be set by the project 
45424 ];
45425
45426
45427 Roo.HtmlEditorCore.swapCodes   =[ 
45428     [    8211, "&#8211;" ], 
45429     [    8212, "&#8212;" ], 
45430     [    8216,  "'" ],  
45431     [    8217, "'" ],  
45432     [    8220, '"' ],  
45433     [    8221, '"' ],  
45434     [    8226, "*" ],  
45435     [    8230, "..." ]
45436 ]; 
45437
45438     //<script type="text/javascript">
45439
45440 /*
45441  * Ext JS Library 1.1.1
45442  * Copyright(c) 2006-2007, Ext JS, LLC.
45443  * Licence LGPL
45444  * 
45445  */
45446  
45447  
45448 Roo.form.HtmlEditor = function(config){
45449     
45450     
45451     
45452     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45453     
45454     if (!this.toolbars) {
45455         this.toolbars = [];
45456     }
45457     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45458     
45459     
45460 };
45461
45462 /**
45463  * @class Roo.form.HtmlEditor
45464  * @extends Roo.form.Field
45465  * Provides a lightweight HTML Editor component.
45466  *
45467  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45468  * 
45469  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45470  * supported by this editor.</b><br/><br/>
45471  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45472  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45473  */
45474 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45475     /**
45476      * @cfg {Boolean} clearUp
45477      */
45478     clearUp : true,
45479       /**
45480      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45481      */
45482     toolbars : false,
45483    
45484      /**
45485      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45486      *                        Roo.resizable.
45487      */
45488     resizable : false,
45489      /**
45490      * @cfg {Number} height (in pixels)
45491      */   
45492     height: 300,
45493    /**
45494      * @cfg {Number} width (in pixels)
45495      */   
45496     width: 500,
45497     
45498     /**
45499      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45500      * 
45501      */
45502     stylesheets: false,
45503     
45504     
45505      /**
45506      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45507      * 
45508      */
45509     cblack: false,
45510     /**
45511      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45512      * 
45513      */
45514     cwhite: false,
45515     
45516      /**
45517      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45518      * 
45519      */
45520     black: false,
45521     /**
45522      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45523      * 
45524      */
45525     white: false,
45526     /**
45527      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45528      */
45529     allowComments: false,
45530     
45531     // id of frame..
45532     frameId: false,
45533     
45534     // private properties
45535     validationEvent : false,
45536     deferHeight: true,
45537     initialized : false,
45538     activated : false,
45539     
45540     onFocus : Roo.emptyFn,
45541     iframePad:3,
45542     hideMode:'offsets',
45543     
45544     actionMode : 'container', // defaults to hiding it...
45545     
45546     defaultAutoCreate : { // modified by initCompnoent..
45547         tag: "textarea",
45548         style:"width:500px;height:300px;",
45549         autocomplete: "new-password"
45550     },
45551
45552     // private
45553     initComponent : function(){
45554         this.addEvents({
45555             /**
45556              * @event initialize
45557              * Fires when the editor is fully initialized (including the iframe)
45558              * @param {HtmlEditor} this
45559              */
45560             initialize: true,
45561             /**
45562              * @event activate
45563              * Fires when the editor is first receives the focus. Any insertion must wait
45564              * until after this event.
45565              * @param {HtmlEditor} this
45566              */
45567             activate: true,
45568              /**
45569              * @event beforesync
45570              * Fires before the textarea is updated with content from the editor iframe. Return false
45571              * to cancel the sync.
45572              * @param {HtmlEditor} this
45573              * @param {String} html
45574              */
45575             beforesync: true,
45576              /**
45577              * @event beforepush
45578              * Fires before the iframe editor is updated with content from the textarea. Return false
45579              * to cancel the push.
45580              * @param {HtmlEditor} this
45581              * @param {String} html
45582              */
45583             beforepush: true,
45584              /**
45585              * @event sync
45586              * Fires when the textarea is updated with content from the editor iframe.
45587              * @param {HtmlEditor} this
45588              * @param {String} html
45589              */
45590             sync: true,
45591              /**
45592              * @event push
45593              * Fires when the iframe editor is updated with content from the textarea.
45594              * @param {HtmlEditor} this
45595              * @param {String} html
45596              */
45597             push: true,
45598              /**
45599              * @event editmodechange
45600              * Fires when the editor switches edit modes
45601              * @param {HtmlEditor} this
45602              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45603              */
45604             editmodechange: true,
45605             /**
45606              * @event editorevent
45607              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45608              * @param {HtmlEditor} this
45609              */
45610             editorevent: true,
45611             /**
45612              * @event firstfocus
45613              * Fires when on first focus - needed by toolbars..
45614              * @param {HtmlEditor} this
45615              */
45616             firstfocus: true,
45617             /**
45618              * @event autosave
45619              * Auto save the htmlEditor value as a file into Events
45620              * @param {HtmlEditor} this
45621              */
45622             autosave: true,
45623             /**
45624              * @event savedpreview
45625              * preview the saved version of htmlEditor
45626              * @param {HtmlEditor} this
45627              */
45628             savedpreview: true,
45629             
45630             /**
45631             * @event stylesheetsclick
45632             * Fires when press the Sytlesheets button
45633             * @param {Roo.HtmlEditorCore} this
45634             */
45635             stylesheetsclick: true
45636         });
45637         this.defaultAutoCreate =  {
45638             tag: "textarea",
45639             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45640             autocomplete: "new-password"
45641         };
45642     },
45643
45644     /**
45645      * Protected method that will not generally be called directly. It
45646      * is called when the editor creates its toolbar. Override this method if you need to
45647      * add custom toolbar buttons.
45648      * @param {HtmlEditor} editor
45649      */
45650     createToolbar : function(editor){
45651         Roo.log("create toolbars");
45652         if (!editor.toolbars || !editor.toolbars.length) {
45653             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45654         }
45655         
45656         for (var i =0 ; i < editor.toolbars.length;i++) {
45657             editor.toolbars[i] = Roo.factory(
45658                     typeof(editor.toolbars[i]) == 'string' ?
45659                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45660                 Roo.form.HtmlEditor);
45661             editor.toolbars[i].init(editor);
45662         }
45663          
45664         
45665     },
45666
45667      
45668     // private
45669     onRender : function(ct, position)
45670     {
45671         var _t = this;
45672         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45673         
45674         this.wrap = this.el.wrap({
45675             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45676         });
45677         
45678         this.editorcore.onRender(ct, position);
45679          
45680         if (this.resizable) {
45681             this.resizeEl = new Roo.Resizable(this.wrap, {
45682                 pinned : true,
45683                 wrap: true,
45684                 dynamic : true,
45685                 minHeight : this.height,
45686                 height: this.height,
45687                 handles : this.resizable,
45688                 width: this.width,
45689                 listeners : {
45690                     resize : function(r, w, h) {
45691                         _t.onResize(w,h); // -something
45692                     }
45693                 }
45694             });
45695             
45696         }
45697         this.createToolbar(this);
45698        
45699         
45700         if(!this.width){
45701             this.setSize(this.wrap.getSize());
45702         }
45703         if (this.resizeEl) {
45704             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45705             // should trigger onReize..
45706         }
45707         
45708         this.keyNav = new Roo.KeyNav(this.el, {
45709             
45710             "tab" : function(e){
45711                 e.preventDefault();
45712                 
45713                 var value = this.getValue();
45714                 
45715                 var start = this.el.dom.selectionStart;
45716                 var end = this.el.dom.selectionEnd;
45717                 
45718                 if(!e.shiftKey){
45719                     
45720                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45721                     this.el.dom.setSelectionRange(end + 1, end + 1);
45722                     return;
45723                 }
45724                 
45725                 var f = value.substring(0, start).split("\t");
45726                 
45727                 if(f.pop().length != 0){
45728                     return;
45729                 }
45730                 
45731                 this.setValue(f.join("\t") + value.substring(end));
45732                 this.el.dom.setSelectionRange(start - 1, start - 1);
45733                 
45734             },
45735             
45736             "home" : function(e){
45737                 e.preventDefault();
45738                 
45739                 var curr = this.el.dom.selectionStart;
45740                 var lines = this.getValue().split("\n");
45741                 
45742                 if(!lines.length){
45743                     return;
45744                 }
45745                 
45746                 if(e.ctrlKey){
45747                     this.el.dom.setSelectionRange(0, 0);
45748                     return;
45749                 }
45750                 
45751                 var pos = 0;
45752                 
45753                 for (var i = 0; i < lines.length;i++) {
45754                     pos += lines[i].length;
45755                     
45756                     if(i != 0){
45757                         pos += 1;
45758                     }
45759                     
45760                     if(pos < curr){
45761                         continue;
45762                     }
45763                     
45764                     pos -= lines[i].length;
45765                     
45766                     break;
45767                 }
45768                 
45769                 if(!e.shiftKey){
45770                     this.el.dom.setSelectionRange(pos, pos);
45771                     return;
45772                 }
45773                 
45774                 this.el.dom.selectionStart = pos;
45775                 this.el.dom.selectionEnd = curr;
45776             },
45777             
45778             "end" : function(e){
45779                 e.preventDefault();
45780                 
45781                 var curr = this.el.dom.selectionStart;
45782                 var lines = this.getValue().split("\n");
45783                 
45784                 if(!lines.length){
45785                     return;
45786                 }
45787                 
45788                 if(e.ctrlKey){
45789                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45790                     return;
45791                 }
45792                 
45793                 var pos = 0;
45794                 
45795                 for (var i = 0; i < lines.length;i++) {
45796                     
45797                     pos += lines[i].length;
45798                     
45799                     if(i != 0){
45800                         pos += 1;
45801                     }
45802                     
45803                     if(pos < curr){
45804                         continue;
45805                     }
45806                     
45807                     break;
45808                 }
45809                 
45810                 if(!e.shiftKey){
45811                     this.el.dom.setSelectionRange(pos, pos);
45812                     return;
45813                 }
45814                 
45815                 this.el.dom.selectionStart = curr;
45816                 this.el.dom.selectionEnd = pos;
45817             },
45818
45819             scope : this,
45820
45821             doRelay : function(foo, bar, hname){
45822                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45823             },
45824
45825             forceKeyDown: true
45826         });
45827         
45828 //        if(this.autosave && this.w){
45829 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45830 //        }
45831     },
45832
45833     // private
45834     onResize : function(w, h)
45835     {
45836         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45837         var ew = false;
45838         var eh = false;
45839         
45840         if(this.el ){
45841             if(typeof w == 'number'){
45842                 var aw = w - this.wrap.getFrameWidth('lr');
45843                 this.el.setWidth(this.adjustWidth('textarea', aw));
45844                 ew = aw;
45845             }
45846             if(typeof h == 'number'){
45847                 var tbh = 0;
45848                 for (var i =0; i < this.toolbars.length;i++) {
45849                     // fixme - ask toolbars for heights?
45850                     tbh += this.toolbars[i].tb.el.getHeight();
45851                     if (this.toolbars[i].footer) {
45852                         tbh += this.toolbars[i].footer.el.getHeight();
45853                     }
45854                 }
45855                 
45856                 
45857                 
45858                 
45859                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45860                 ah -= 5; // knock a few pixes off for look..
45861 //                Roo.log(ah);
45862                 this.el.setHeight(this.adjustWidth('textarea', ah));
45863                 var eh = ah;
45864             }
45865         }
45866         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45867         this.editorcore.onResize(ew,eh);
45868         
45869     },
45870
45871     /**
45872      * Toggles the editor between standard and source edit mode.
45873      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45874      */
45875     toggleSourceEdit : function(sourceEditMode)
45876     {
45877         this.editorcore.toggleSourceEdit(sourceEditMode);
45878         
45879         if(this.editorcore.sourceEditMode){
45880             Roo.log('editor - showing textarea');
45881             
45882 //            Roo.log('in');
45883 //            Roo.log(this.syncValue());
45884             this.editorcore.syncValue();
45885             this.el.removeClass('x-hidden');
45886             this.el.dom.removeAttribute('tabIndex');
45887             this.el.focus();
45888             
45889             for (var i = 0; i < this.toolbars.length; i++) {
45890                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45891                     this.toolbars[i].tb.hide();
45892                     this.toolbars[i].footer.hide();
45893                 }
45894             }
45895             
45896         }else{
45897             Roo.log('editor - hiding textarea');
45898 //            Roo.log('out')
45899 //            Roo.log(this.pushValue()); 
45900             this.editorcore.pushValue();
45901             
45902             this.el.addClass('x-hidden');
45903             this.el.dom.setAttribute('tabIndex', -1);
45904             
45905             for (var i = 0; i < this.toolbars.length; i++) {
45906                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45907                     this.toolbars[i].tb.show();
45908                     this.toolbars[i].footer.show();
45909                 }
45910             }
45911             
45912             //this.deferFocus();
45913         }
45914         
45915         this.setSize(this.wrap.getSize());
45916         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45917         
45918         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45919     },
45920  
45921     // private (for BoxComponent)
45922     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45923
45924     // private (for BoxComponent)
45925     getResizeEl : function(){
45926         return this.wrap;
45927     },
45928
45929     // private (for BoxComponent)
45930     getPositionEl : function(){
45931         return this.wrap;
45932     },
45933
45934     // private
45935     initEvents : function(){
45936         this.originalValue = this.getValue();
45937     },
45938
45939     /**
45940      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45941      * @method
45942      */
45943     markInvalid : Roo.emptyFn,
45944     /**
45945      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45946      * @method
45947      */
45948     clearInvalid : Roo.emptyFn,
45949
45950     setValue : function(v){
45951         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45952         this.editorcore.pushValue();
45953     },
45954
45955      
45956     // private
45957     deferFocus : function(){
45958         this.focus.defer(10, this);
45959     },
45960
45961     // doc'ed in Field
45962     focus : function(){
45963         this.editorcore.focus();
45964         
45965     },
45966       
45967
45968     // private
45969     onDestroy : function(){
45970         
45971         
45972         
45973         if(this.rendered){
45974             
45975             for (var i =0; i < this.toolbars.length;i++) {
45976                 // fixme - ask toolbars for heights?
45977                 this.toolbars[i].onDestroy();
45978             }
45979             
45980             this.wrap.dom.innerHTML = '';
45981             this.wrap.remove();
45982         }
45983     },
45984
45985     // private
45986     onFirstFocus : function(){
45987         //Roo.log("onFirstFocus");
45988         this.editorcore.onFirstFocus();
45989          for (var i =0; i < this.toolbars.length;i++) {
45990             this.toolbars[i].onFirstFocus();
45991         }
45992         
45993     },
45994     
45995     // private
45996     syncValue : function()
45997     {
45998         this.editorcore.syncValue();
45999     },
46000     
46001     pushValue : function()
46002     {
46003         this.editorcore.pushValue();
46004     },
46005     
46006     setStylesheets : function(stylesheets)
46007     {
46008         this.editorcore.setStylesheets(stylesheets);
46009     },
46010     
46011     removeStylesheets : function()
46012     {
46013         this.editorcore.removeStylesheets();
46014     }
46015      
46016     
46017     // hide stuff that is not compatible
46018     /**
46019      * @event blur
46020      * @hide
46021      */
46022     /**
46023      * @event change
46024      * @hide
46025      */
46026     /**
46027      * @event focus
46028      * @hide
46029      */
46030     /**
46031      * @event specialkey
46032      * @hide
46033      */
46034     /**
46035      * @cfg {String} fieldClass @hide
46036      */
46037     /**
46038      * @cfg {String} focusClass @hide
46039      */
46040     /**
46041      * @cfg {String} autoCreate @hide
46042      */
46043     /**
46044      * @cfg {String} inputType @hide
46045      */
46046     /**
46047      * @cfg {String} invalidClass @hide
46048      */
46049     /**
46050      * @cfg {String} invalidText @hide
46051      */
46052     /**
46053      * @cfg {String} msgFx @hide
46054      */
46055     /**
46056      * @cfg {String} validateOnBlur @hide
46057      */
46058 });
46059  
46060     // <script type="text/javascript">
46061 /*
46062  * Based on
46063  * Ext JS Library 1.1.1
46064  * Copyright(c) 2006-2007, Ext JS, LLC.
46065  *  
46066  
46067  */
46068
46069 /**
46070  * @class Roo.form.HtmlEditorToolbar1
46071  * Basic Toolbar
46072  * 
46073  * Usage:
46074  *
46075  new Roo.form.HtmlEditor({
46076     ....
46077     toolbars : [
46078         new Roo.form.HtmlEditorToolbar1({
46079             disable : { fonts: 1 , format: 1, ..., ... , ...],
46080             btns : [ .... ]
46081         })
46082     }
46083      
46084  * 
46085  * @cfg {Object} disable List of elements to disable..
46086  * @cfg {Array} btns List of additional buttons.
46087  * 
46088  * 
46089  * NEEDS Extra CSS? 
46090  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46091  */
46092  
46093 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46094 {
46095     
46096     Roo.apply(this, config);
46097     
46098     // default disabled, based on 'good practice'..
46099     this.disable = this.disable || {};
46100     Roo.applyIf(this.disable, {
46101         fontSize : true,
46102         colors : true,
46103         specialElements : true
46104     });
46105     
46106     
46107     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46108     // dont call parent... till later.
46109 }
46110
46111 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46112     
46113     tb: false,
46114     
46115     rendered: false,
46116     
46117     editor : false,
46118     editorcore : false,
46119     /**
46120      * @cfg {Object} disable  List of toolbar elements to disable
46121          
46122      */
46123     disable : false,
46124     
46125     
46126      /**
46127      * @cfg {String} createLinkText The default text for the create link prompt
46128      */
46129     createLinkText : 'Please enter the URL for the link:',
46130     /**
46131      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46132      */
46133     defaultLinkValue : 'http:/'+'/',
46134    
46135     
46136       /**
46137      * @cfg {Array} fontFamilies An array of available font families
46138      */
46139     fontFamilies : [
46140         'Arial',
46141         'Courier New',
46142         'Tahoma',
46143         'Times New Roman',
46144         'Verdana'
46145     ],
46146     
46147     specialChars : [
46148            "&#169;",
46149           "&#174;",     
46150           "&#8482;",    
46151           "&#163;" ,    
46152          // "&#8212;",    
46153           "&#8230;",    
46154           "&#247;" ,    
46155         //  "&#225;" ,     ?? a acute?
46156            "&#8364;"    , //Euro
46157        //   "&#8220;"    ,
46158         //  "&#8221;"    ,
46159         //  "&#8226;"    ,
46160           "&#176;"  //   , // degrees
46161
46162          // "&#233;"     , // e ecute
46163          // "&#250;"     , // u ecute?
46164     ],
46165     
46166     specialElements : [
46167         {
46168             text: "Insert Table",
46169             xtype: 'MenuItem',
46170             xns : Roo.Menu,
46171             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46172                 
46173         },
46174         {    
46175             text: "Insert Image",
46176             xtype: 'MenuItem',
46177             xns : Roo.Menu,
46178             ihtml : '<img src="about:blank"/>'
46179             
46180         }
46181         
46182          
46183     ],
46184     
46185     
46186     inputElements : [ 
46187             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46188             "input:submit", "input:button", "select", "textarea", "label" ],
46189     formats : [
46190         ["p"] ,  
46191         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46192         ["pre"],[ "code"], 
46193         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46194         ['div'],['span'],
46195         ['sup'],['sub']
46196     ],
46197     
46198     cleanStyles : [
46199         "font-size"
46200     ],
46201      /**
46202      * @cfg {String} defaultFont default font to use.
46203      */
46204     defaultFont: 'tahoma',
46205    
46206     fontSelect : false,
46207     
46208     
46209     formatCombo : false,
46210     
46211     init : function(editor)
46212     {
46213         this.editor = editor;
46214         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46215         var editorcore = this.editorcore;
46216         
46217         var _t = this;
46218         
46219         var fid = editorcore.frameId;
46220         var etb = this;
46221         function btn(id, toggle, handler){
46222             var xid = fid + '-'+ id ;
46223             return {
46224                 id : xid,
46225                 cmd : id,
46226                 cls : 'x-btn-icon x-edit-'+id,
46227                 enableToggle:toggle !== false,
46228                 scope: _t, // was editor...
46229                 handler:handler||_t.relayBtnCmd,
46230                 clickEvent:'mousedown',
46231                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46232                 tabIndex:-1
46233             };
46234         }
46235         
46236         
46237         
46238         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46239         this.tb = tb;
46240          // stop form submits
46241         tb.el.on('click', function(e){
46242             e.preventDefault(); // what does this do?
46243         });
46244
46245         if(!this.disable.font) { // && !Roo.isSafari){
46246             /* why no safari for fonts 
46247             editor.fontSelect = tb.el.createChild({
46248                 tag:'select',
46249                 tabIndex: -1,
46250                 cls:'x-font-select',
46251                 html: this.createFontOptions()
46252             });
46253             
46254             editor.fontSelect.on('change', function(){
46255                 var font = editor.fontSelect.dom.value;
46256                 editor.relayCmd('fontname', font);
46257                 editor.deferFocus();
46258             }, editor);
46259             
46260             tb.add(
46261                 editor.fontSelect.dom,
46262                 '-'
46263             );
46264             */
46265             
46266         };
46267         if(!this.disable.formats){
46268             this.formatCombo = new Roo.form.ComboBox({
46269                 store: new Roo.data.SimpleStore({
46270                     id : 'tag',
46271                     fields: ['tag'],
46272                     data : this.formats // from states.js
46273                 }),
46274                 blockFocus : true,
46275                 name : '',
46276                 //autoCreate : {tag: "div",  size: "20"},
46277                 displayField:'tag',
46278                 typeAhead: false,
46279                 mode: 'local',
46280                 editable : false,
46281                 triggerAction: 'all',
46282                 emptyText:'Add tag',
46283                 selectOnFocus:true,
46284                 width:135,
46285                 listeners : {
46286                     'select': function(c, r, i) {
46287                         editorcore.insertTag(r.get('tag'));
46288                         editor.focus();
46289                     }
46290                 }
46291
46292             });
46293             tb.addField(this.formatCombo);
46294             
46295         }
46296         
46297         if(!this.disable.format){
46298             tb.add(
46299                 btn('bold'),
46300                 btn('italic'),
46301                 btn('underline'),
46302                 btn('strikethrough')
46303             );
46304         };
46305         if(!this.disable.fontSize){
46306             tb.add(
46307                 '-',
46308                 
46309                 
46310                 btn('increasefontsize', false, editorcore.adjustFont),
46311                 btn('decreasefontsize', false, editorcore.adjustFont)
46312             );
46313         };
46314         
46315         
46316         if(!this.disable.colors){
46317             tb.add(
46318                 '-', {
46319                     id:editorcore.frameId +'-forecolor',
46320                     cls:'x-btn-icon x-edit-forecolor',
46321                     clickEvent:'mousedown',
46322                     tooltip: this.buttonTips['forecolor'] || undefined,
46323                     tabIndex:-1,
46324                     menu : new Roo.menu.ColorMenu({
46325                         allowReselect: true,
46326                         focus: Roo.emptyFn,
46327                         value:'000000',
46328                         plain:true,
46329                         selectHandler: function(cp, color){
46330                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46331                             editor.deferFocus();
46332                         },
46333                         scope: editorcore,
46334                         clickEvent:'mousedown'
46335                     })
46336                 }, {
46337                     id:editorcore.frameId +'backcolor',
46338                     cls:'x-btn-icon x-edit-backcolor',
46339                     clickEvent:'mousedown',
46340                     tooltip: this.buttonTips['backcolor'] || undefined,
46341                     tabIndex:-1,
46342                     menu : new Roo.menu.ColorMenu({
46343                         focus: Roo.emptyFn,
46344                         value:'FFFFFF',
46345                         plain:true,
46346                         allowReselect: true,
46347                         selectHandler: function(cp, color){
46348                             if(Roo.isGecko){
46349                                 editorcore.execCmd('useCSS', false);
46350                                 editorcore.execCmd('hilitecolor', color);
46351                                 editorcore.execCmd('useCSS', true);
46352                                 editor.deferFocus();
46353                             }else{
46354                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46355                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46356                                 editor.deferFocus();
46357                             }
46358                         },
46359                         scope:editorcore,
46360                         clickEvent:'mousedown'
46361                     })
46362                 }
46363             );
46364         };
46365         // now add all the items...
46366         
46367
46368         if(!this.disable.alignments){
46369             tb.add(
46370                 '-',
46371                 btn('justifyleft'),
46372                 btn('justifycenter'),
46373                 btn('justifyright')
46374             );
46375         };
46376
46377         //if(!Roo.isSafari){
46378             if(!this.disable.links){
46379                 tb.add(
46380                     '-',
46381                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46382                 );
46383             };
46384
46385             if(!this.disable.lists){
46386                 tb.add(
46387                     '-',
46388                     btn('insertorderedlist'),
46389                     btn('insertunorderedlist')
46390                 );
46391             }
46392             if(!this.disable.sourceEdit){
46393                 tb.add(
46394                     '-',
46395                     btn('sourceedit', true, function(btn){
46396                         this.toggleSourceEdit(btn.pressed);
46397                     })
46398                 );
46399             }
46400         //}
46401         
46402         var smenu = { };
46403         // special menu.. - needs to be tidied up..
46404         if (!this.disable.special) {
46405             smenu = {
46406                 text: "&#169;",
46407                 cls: 'x-edit-none',
46408                 
46409                 menu : {
46410                     items : []
46411                 }
46412             };
46413             for (var i =0; i < this.specialChars.length; i++) {
46414                 smenu.menu.items.push({
46415                     
46416                     html: this.specialChars[i],
46417                     handler: function(a,b) {
46418                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46419                         //editor.insertAtCursor(a.html);
46420                         
46421                     },
46422                     tabIndex:-1
46423                 });
46424             }
46425             
46426             
46427             tb.add(smenu);
46428             
46429             
46430         }
46431         
46432         var cmenu = { };
46433         if (!this.disable.cleanStyles) {
46434             cmenu = {
46435                 cls: 'x-btn-icon x-btn-clear',
46436                 
46437                 menu : {
46438                     items : []
46439                 }
46440             };
46441             for (var i =0; i < this.cleanStyles.length; i++) {
46442                 cmenu.menu.items.push({
46443                     actiontype : this.cleanStyles[i],
46444                     html: 'Remove ' + this.cleanStyles[i],
46445                     handler: function(a,b) {
46446 //                        Roo.log(a);
46447 //                        Roo.log(b);
46448                         var c = Roo.get(editorcore.doc.body);
46449                         c.select('[style]').each(function(s) {
46450                             s.dom.style.removeProperty(a.actiontype);
46451                         });
46452                         editorcore.syncValue();
46453                     },
46454                     tabIndex:-1
46455                 });
46456             }
46457              cmenu.menu.items.push({
46458                 actiontype : 'tablewidths',
46459                 html: 'Remove Table Widths',
46460                 handler: function(a,b) {
46461                     editorcore.cleanTableWidths();
46462                     editorcore.syncValue();
46463                 },
46464                 tabIndex:-1
46465             });
46466             cmenu.menu.items.push({
46467                 actiontype : 'word',
46468                 html: 'Remove MS Word Formating',
46469                 handler: function(a,b) {
46470                     editorcore.cleanWord();
46471                     editorcore.syncValue();
46472                 },
46473                 tabIndex:-1
46474             });
46475             
46476             cmenu.menu.items.push({
46477                 actiontype : 'all',
46478                 html: 'Remove All Styles',
46479                 handler: function(a,b) {
46480                     
46481                     var c = Roo.get(editorcore.doc.body);
46482                     c.select('[style]').each(function(s) {
46483                         s.dom.removeAttribute('style');
46484                     });
46485                     editorcore.syncValue();
46486                 },
46487                 tabIndex:-1
46488             });
46489             
46490             cmenu.menu.items.push({
46491                 actiontype : 'all',
46492                 html: 'Remove All CSS Classes',
46493                 handler: function(a,b) {
46494                     
46495                     var c = Roo.get(editorcore.doc.body);
46496                     c.select('[class]').each(function(s) {
46497                         s.dom.removeAttribute('class');
46498                     });
46499                     editorcore.cleanWord();
46500                     editorcore.syncValue();
46501                 },
46502                 tabIndex:-1
46503             });
46504             
46505              cmenu.menu.items.push({
46506                 actiontype : 'tidy',
46507                 html: 'Tidy HTML Source',
46508                 handler: function(a,b) {
46509                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46510                     editorcore.syncValue();
46511                 },
46512                 tabIndex:-1
46513             });
46514             
46515             
46516             tb.add(cmenu);
46517         }
46518          
46519         if (!this.disable.specialElements) {
46520             var semenu = {
46521                 text: "Other;",
46522                 cls: 'x-edit-none',
46523                 menu : {
46524                     items : []
46525                 }
46526             };
46527             for (var i =0; i < this.specialElements.length; i++) {
46528                 semenu.menu.items.push(
46529                     Roo.apply({ 
46530                         handler: function(a,b) {
46531                             editor.insertAtCursor(this.ihtml);
46532                         }
46533                     }, this.specialElements[i])
46534                 );
46535                     
46536             }
46537             
46538             tb.add(semenu);
46539             
46540             
46541         }
46542          
46543         
46544         if (this.btns) {
46545             for(var i =0; i< this.btns.length;i++) {
46546                 var b = Roo.factory(this.btns[i],Roo.form);
46547                 b.cls =  'x-edit-none';
46548                 
46549                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46550                     b.cls += ' x-init-enable';
46551                 }
46552                 
46553                 b.scope = editorcore;
46554                 tb.add(b);
46555             }
46556         
46557         }
46558         
46559         
46560         
46561         // disable everything...
46562         
46563         this.tb.items.each(function(item){
46564             
46565            if(
46566                 item.id != editorcore.frameId+ '-sourceedit' && 
46567                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46568             ){
46569                 
46570                 item.disable();
46571             }
46572         });
46573         this.rendered = true;
46574         
46575         // the all the btns;
46576         editor.on('editorevent', this.updateToolbar, this);
46577         // other toolbars need to implement this..
46578         //editor.on('editmodechange', this.updateToolbar, this);
46579     },
46580     
46581     
46582     relayBtnCmd : function(btn) {
46583         this.editorcore.relayCmd(btn.cmd);
46584     },
46585     // private used internally
46586     createLink : function(){
46587         Roo.log("create link?");
46588         var url = prompt(this.createLinkText, this.defaultLinkValue);
46589         if(url && url != 'http:/'+'/'){
46590             this.editorcore.relayCmd('createlink', url);
46591         }
46592     },
46593
46594     
46595     /**
46596      * Protected method that will not generally be called directly. It triggers
46597      * a toolbar update by reading the markup state of the current selection in the editor.
46598      */
46599     updateToolbar: function(){
46600
46601         if(!this.editorcore.activated){
46602             this.editor.onFirstFocus();
46603             return;
46604         }
46605
46606         var btns = this.tb.items.map, 
46607             doc = this.editorcore.doc,
46608             frameId = this.editorcore.frameId;
46609
46610         if(!this.disable.font && !Roo.isSafari){
46611             /*
46612             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46613             if(name != this.fontSelect.dom.value){
46614                 this.fontSelect.dom.value = name;
46615             }
46616             */
46617         }
46618         if(!this.disable.format){
46619             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46620             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46621             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46622             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46623         }
46624         if(!this.disable.alignments){
46625             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46626             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46627             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46628         }
46629         if(!Roo.isSafari && !this.disable.lists){
46630             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46631             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46632         }
46633         
46634         var ans = this.editorcore.getAllAncestors();
46635         if (this.formatCombo) {
46636             
46637             
46638             var store = this.formatCombo.store;
46639             this.formatCombo.setValue("");
46640             for (var i =0; i < ans.length;i++) {
46641                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46642                     // select it..
46643                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46644                     break;
46645                 }
46646             }
46647         }
46648         
46649         
46650         
46651         // hides menus... - so this cant be on a menu...
46652         Roo.menu.MenuMgr.hideAll();
46653
46654         //this.editorsyncValue();
46655     },
46656    
46657     
46658     createFontOptions : function(){
46659         var buf = [], fs = this.fontFamilies, ff, lc;
46660         
46661         
46662         
46663         for(var i = 0, len = fs.length; i< len; i++){
46664             ff = fs[i];
46665             lc = ff.toLowerCase();
46666             buf.push(
46667                 '<option value="',lc,'" style="font-family:',ff,';"',
46668                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46669                     ff,
46670                 '</option>'
46671             );
46672         }
46673         return buf.join('');
46674     },
46675     
46676     toggleSourceEdit : function(sourceEditMode){
46677         
46678         Roo.log("toolbar toogle");
46679         if(sourceEditMode === undefined){
46680             sourceEditMode = !this.sourceEditMode;
46681         }
46682         this.sourceEditMode = sourceEditMode === true;
46683         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46684         // just toggle the button?
46685         if(btn.pressed !== this.sourceEditMode){
46686             btn.toggle(this.sourceEditMode);
46687             return;
46688         }
46689         
46690         if(sourceEditMode){
46691             Roo.log("disabling buttons");
46692             this.tb.items.each(function(item){
46693                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46694                     item.disable();
46695                 }
46696             });
46697           
46698         }else{
46699             Roo.log("enabling buttons");
46700             if(this.editorcore.initialized){
46701                 this.tb.items.each(function(item){
46702                     item.enable();
46703                 });
46704             }
46705             
46706         }
46707         Roo.log("calling toggole on editor");
46708         // tell the editor that it's been pressed..
46709         this.editor.toggleSourceEdit(sourceEditMode);
46710        
46711     },
46712      /**
46713      * Object collection of toolbar tooltips for the buttons in the editor. The key
46714      * is the command id associated with that button and the value is a valid QuickTips object.
46715      * For example:
46716 <pre><code>
46717 {
46718     bold : {
46719         title: 'Bold (Ctrl+B)',
46720         text: 'Make the selected text bold.',
46721         cls: 'x-html-editor-tip'
46722     },
46723     italic : {
46724         title: 'Italic (Ctrl+I)',
46725         text: 'Make the selected text italic.',
46726         cls: 'x-html-editor-tip'
46727     },
46728     ...
46729 </code></pre>
46730     * @type Object
46731      */
46732     buttonTips : {
46733         bold : {
46734             title: 'Bold (Ctrl+B)',
46735             text: 'Make the selected text bold.',
46736             cls: 'x-html-editor-tip'
46737         },
46738         italic : {
46739             title: 'Italic (Ctrl+I)',
46740             text: 'Make the selected text italic.',
46741             cls: 'x-html-editor-tip'
46742         },
46743         underline : {
46744             title: 'Underline (Ctrl+U)',
46745             text: 'Underline the selected text.',
46746             cls: 'x-html-editor-tip'
46747         },
46748         strikethrough : {
46749             title: 'Strikethrough',
46750             text: 'Strikethrough the selected text.',
46751             cls: 'x-html-editor-tip'
46752         },
46753         increasefontsize : {
46754             title: 'Grow Text',
46755             text: 'Increase the font size.',
46756             cls: 'x-html-editor-tip'
46757         },
46758         decreasefontsize : {
46759             title: 'Shrink Text',
46760             text: 'Decrease the font size.',
46761             cls: 'x-html-editor-tip'
46762         },
46763         backcolor : {
46764             title: 'Text Highlight Color',
46765             text: 'Change the background color of the selected text.',
46766             cls: 'x-html-editor-tip'
46767         },
46768         forecolor : {
46769             title: 'Font Color',
46770             text: 'Change the color of the selected text.',
46771             cls: 'x-html-editor-tip'
46772         },
46773         justifyleft : {
46774             title: 'Align Text Left',
46775             text: 'Align text to the left.',
46776             cls: 'x-html-editor-tip'
46777         },
46778         justifycenter : {
46779             title: 'Center Text',
46780             text: 'Center text in the editor.',
46781             cls: 'x-html-editor-tip'
46782         },
46783         justifyright : {
46784             title: 'Align Text Right',
46785             text: 'Align text to the right.',
46786             cls: 'x-html-editor-tip'
46787         },
46788         insertunorderedlist : {
46789             title: 'Bullet List',
46790             text: 'Start a bulleted list.',
46791             cls: 'x-html-editor-tip'
46792         },
46793         insertorderedlist : {
46794             title: 'Numbered List',
46795             text: 'Start a numbered list.',
46796             cls: 'x-html-editor-tip'
46797         },
46798         createlink : {
46799             title: 'Hyperlink',
46800             text: 'Make the selected text a hyperlink.',
46801             cls: 'x-html-editor-tip'
46802         },
46803         sourceedit : {
46804             title: 'Source Edit',
46805             text: 'Switch to source editing mode.',
46806             cls: 'x-html-editor-tip'
46807         }
46808     },
46809     // private
46810     onDestroy : function(){
46811         if(this.rendered){
46812             
46813             this.tb.items.each(function(item){
46814                 if(item.menu){
46815                     item.menu.removeAll();
46816                     if(item.menu.el){
46817                         item.menu.el.destroy();
46818                     }
46819                 }
46820                 item.destroy();
46821             });
46822              
46823         }
46824     },
46825     onFirstFocus: function() {
46826         this.tb.items.each(function(item){
46827            item.enable();
46828         });
46829     }
46830 });
46831
46832
46833
46834
46835 // <script type="text/javascript">
46836 /*
46837  * Based on
46838  * Ext JS Library 1.1.1
46839  * Copyright(c) 2006-2007, Ext JS, LLC.
46840  *  
46841  
46842  */
46843
46844  
46845 /**
46846  * @class Roo.form.HtmlEditor.ToolbarContext
46847  * Context Toolbar
46848  * 
46849  * Usage:
46850  *
46851  new Roo.form.HtmlEditor({
46852     ....
46853     toolbars : [
46854         { xtype: 'ToolbarStandard', styles : {} }
46855         { xtype: 'ToolbarContext', disable : {} }
46856     ]
46857 })
46858
46859      
46860  * 
46861  * @config : {Object} disable List of elements to disable.. (not done yet.)
46862  * @config : {Object} styles  Map of styles available.
46863  * 
46864  */
46865
46866 Roo.form.HtmlEditor.ToolbarContext = function(config)
46867 {
46868     
46869     Roo.apply(this, config);
46870     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46871     // dont call parent... till later.
46872     this.styles = this.styles || {};
46873 }
46874
46875  
46876
46877 Roo.form.HtmlEditor.ToolbarContext.types = {
46878     'IMG' : {
46879         width : {
46880             title: "Width",
46881             width: 40
46882         },
46883         height:  {
46884             title: "Height",
46885             width: 40
46886         },
46887         align: {
46888             title: "Align",
46889             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46890             width : 80
46891             
46892         },
46893         border: {
46894             title: "Border",
46895             width: 40
46896         },
46897         alt: {
46898             title: "Alt",
46899             width: 120
46900         },
46901         src : {
46902             title: "Src",
46903             width: 220
46904         }
46905         
46906     },
46907     'A' : {
46908         name : {
46909             title: "Name",
46910             width: 50
46911         },
46912         target:  {
46913             title: "Target",
46914             width: 120
46915         },
46916         href:  {
46917             title: "Href",
46918             width: 220
46919         } // border?
46920         
46921     },
46922     'TABLE' : {
46923         rows : {
46924             title: "Rows",
46925             width: 20
46926         },
46927         cols : {
46928             title: "Cols",
46929             width: 20
46930         },
46931         width : {
46932             title: "Width",
46933             width: 40
46934         },
46935         height : {
46936             title: "Height",
46937             width: 40
46938         },
46939         border : {
46940             title: "Border",
46941             width: 20
46942         }
46943     },
46944     'TD' : {
46945         width : {
46946             title: "Width",
46947             width: 40
46948         },
46949         height : {
46950             title: "Height",
46951             width: 40
46952         },   
46953         align: {
46954             title: "Align",
46955             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46956             width: 80
46957         },
46958         valign: {
46959             title: "Valign",
46960             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46961             width: 80
46962         },
46963         colspan: {
46964             title: "Colspan",
46965             width: 20
46966             
46967         },
46968          'font-family'  : {
46969             title : "Font",
46970             style : 'fontFamily',
46971             displayField: 'display',
46972             optname : 'font-family',
46973             width: 140
46974         }
46975     },
46976     'INPUT' : {
46977         name : {
46978             title: "name",
46979             width: 120
46980         },
46981         value : {
46982             title: "Value",
46983             width: 120
46984         },
46985         width : {
46986             title: "Width",
46987             width: 40
46988         }
46989     },
46990     'LABEL' : {
46991         'for' : {
46992             title: "For",
46993             width: 120
46994         }
46995     },
46996     'TEXTAREA' : {
46997           name : {
46998             title: "name",
46999             width: 120
47000         },
47001         rows : {
47002             title: "Rows",
47003             width: 20
47004         },
47005         cols : {
47006             title: "Cols",
47007             width: 20
47008         }
47009     },
47010     'SELECT' : {
47011         name : {
47012             title: "name",
47013             width: 120
47014         },
47015         selectoptions : {
47016             title: "Options",
47017             width: 200
47018         }
47019     },
47020     
47021     // should we really allow this??
47022     // should this just be 
47023     'BODY' : {
47024         title : {
47025             title: "Title",
47026             width: 200,
47027             disabled : true
47028         }
47029     },
47030     'SPAN' : {
47031         'font-family'  : {
47032             title : "Font",
47033             style : 'fontFamily',
47034             displayField: 'display',
47035             optname : 'font-family',
47036             width: 140
47037         }
47038     },
47039     'DIV' : {
47040         'font-family'  : {
47041             title : "Font",
47042             style : 'fontFamily',
47043             displayField: 'display',
47044             optname : 'font-family',
47045             width: 140
47046         }
47047     },
47048      'P' : {
47049         'font-family'  : {
47050             title : "Font",
47051             style : 'fontFamily',
47052             displayField: 'display',
47053             optname : 'font-family',
47054             width: 140
47055         }
47056     },
47057     
47058     '*' : {
47059         // empty..
47060     }
47061
47062 };
47063
47064 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47065 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47066
47067 Roo.form.HtmlEditor.ToolbarContext.options = {
47068         'font-family'  : [ 
47069                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47070                 [ 'Courier New', 'Courier New'],
47071                 [ 'Tahoma', 'Tahoma'],
47072                 [ 'Times New Roman,serif', 'Times'],
47073                 [ 'Verdana','Verdana' ]
47074         ]
47075 };
47076
47077 // fixme - these need to be configurable..
47078  
47079
47080 //Roo.form.HtmlEditor.ToolbarContext.types
47081
47082
47083 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47084     
47085     tb: false,
47086     
47087     rendered: false,
47088     
47089     editor : false,
47090     editorcore : false,
47091     /**
47092      * @cfg {Object} disable  List of toolbar elements to disable
47093          
47094      */
47095     disable : false,
47096     /**
47097      * @cfg {Object} styles List of styles 
47098      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47099      *
47100      * These must be defined in the page, so they get rendered correctly..
47101      * .headline { }
47102      * TD.underline { }
47103      * 
47104      */
47105     styles : false,
47106     
47107     options: false,
47108     
47109     toolbars : false,
47110     
47111     init : function(editor)
47112     {
47113         this.editor = editor;
47114         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47115         var editorcore = this.editorcore;
47116         
47117         var fid = editorcore.frameId;
47118         var etb = this;
47119         function btn(id, toggle, handler){
47120             var xid = fid + '-'+ id ;
47121             return {
47122                 id : xid,
47123                 cmd : id,
47124                 cls : 'x-btn-icon x-edit-'+id,
47125                 enableToggle:toggle !== false,
47126                 scope: editorcore, // was editor...
47127                 handler:handler||editorcore.relayBtnCmd,
47128                 clickEvent:'mousedown',
47129                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47130                 tabIndex:-1
47131             };
47132         }
47133         // create a new element.
47134         var wdiv = editor.wrap.createChild({
47135                 tag: 'div'
47136             }, editor.wrap.dom.firstChild.nextSibling, true);
47137         
47138         // can we do this more than once??
47139         
47140          // stop form submits
47141       
47142  
47143         // disable everything...
47144         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47145         this.toolbars = {};
47146            
47147         for (var i in  ty) {
47148           
47149             this.toolbars[i] = this.buildToolbar(ty[i],i);
47150         }
47151         this.tb = this.toolbars.BODY;
47152         this.tb.el.show();
47153         this.buildFooter();
47154         this.footer.show();
47155         editor.on('hide', function( ) { this.footer.hide() }, this);
47156         editor.on('show', function( ) { this.footer.show() }, this);
47157         
47158          
47159         this.rendered = true;
47160         
47161         // the all the btns;
47162         editor.on('editorevent', this.updateToolbar, this);
47163         // other toolbars need to implement this..
47164         //editor.on('editmodechange', this.updateToolbar, this);
47165     },
47166     
47167     
47168     
47169     /**
47170      * Protected method that will not generally be called directly. It triggers
47171      * a toolbar update by reading the markup state of the current selection in the editor.
47172      *
47173      * Note you can force an update by calling on('editorevent', scope, false)
47174      */
47175     updateToolbar: function(editor,ev,sel){
47176
47177         //Roo.log(ev);
47178         // capture mouse up - this is handy for selecting images..
47179         // perhaps should go somewhere else...
47180         if(!this.editorcore.activated){
47181              this.editor.onFirstFocus();
47182             return;
47183         }
47184         
47185         
47186         
47187         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47188         // selectNode - might want to handle IE?
47189         if (ev &&
47190             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47191             ev.target && ev.target.tagName == 'IMG') {
47192             // they have click on an image...
47193             // let's see if we can change the selection...
47194             sel = ev.target;
47195          
47196               var nodeRange = sel.ownerDocument.createRange();
47197             try {
47198                 nodeRange.selectNode(sel);
47199             } catch (e) {
47200                 nodeRange.selectNodeContents(sel);
47201             }
47202             //nodeRange.collapse(true);
47203             var s = this.editorcore.win.getSelection();
47204             s.removeAllRanges();
47205             s.addRange(nodeRange);
47206         }  
47207         
47208       
47209         var updateFooter = sel ? false : true;
47210         
47211         
47212         var ans = this.editorcore.getAllAncestors();
47213         
47214         // pick
47215         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47216         
47217         if (!sel) { 
47218             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47219             sel = sel ? sel : this.editorcore.doc.body;
47220             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47221             
47222         }
47223         // pick a menu that exists..
47224         var tn = sel.tagName.toUpperCase();
47225         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47226         
47227         tn = sel.tagName.toUpperCase();
47228         
47229         var lastSel = this.tb.selectedNode;
47230         
47231         this.tb.selectedNode = sel;
47232         
47233         // if current menu does not match..
47234         
47235         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47236                 
47237             this.tb.el.hide();
47238             ///console.log("show: " + tn);
47239             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47240             this.tb.el.show();
47241             // update name
47242             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47243             
47244             
47245             // update attributes
47246             if (this.tb.fields) {
47247                 this.tb.fields.each(function(e) {
47248                     if (e.stylename) {
47249                         e.setValue(sel.style[e.stylename]);
47250                         return;
47251                     } 
47252                    e.setValue(sel.getAttribute(e.attrname));
47253                 });
47254             }
47255             
47256             var hasStyles = false;
47257             for(var i in this.styles) {
47258                 hasStyles = true;
47259                 break;
47260             }
47261             
47262             // update styles
47263             if (hasStyles) { 
47264                 var st = this.tb.fields.item(0);
47265                 
47266                 st.store.removeAll();
47267                
47268                 
47269                 var cn = sel.className.split(/\s+/);
47270                 
47271                 var avs = [];
47272                 if (this.styles['*']) {
47273                     
47274                     Roo.each(this.styles['*'], function(v) {
47275                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47276                     });
47277                 }
47278                 if (this.styles[tn]) { 
47279                     Roo.each(this.styles[tn], function(v) {
47280                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47281                     });
47282                 }
47283                 
47284                 st.store.loadData(avs);
47285                 st.collapse();
47286                 st.setValue(cn);
47287             }
47288             // flag our selected Node.
47289             this.tb.selectedNode = sel;
47290            
47291            
47292             Roo.menu.MenuMgr.hideAll();
47293
47294         }
47295         
47296         if (!updateFooter) {
47297             //this.footDisp.dom.innerHTML = ''; 
47298             return;
47299         }
47300         // update the footer
47301         //
47302         var html = '';
47303         
47304         this.footerEls = ans.reverse();
47305         Roo.each(this.footerEls, function(a,i) {
47306             if (!a) { return; }
47307             html += html.length ? ' &gt; '  :  '';
47308             
47309             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47310             
47311         });
47312        
47313         // 
47314         var sz = this.footDisp.up('td').getSize();
47315         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47316         this.footDisp.dom.style.marginLeft = '5px';
47317         
47318         this.footDisp.dom.style.overflow = 'hidden';
47319         
47320         this.footDisp.dom.innerHTML = html;
47321             
47322         //this.editorsyncValue();
47323     },
47324      
47325     
47326    
47327        
47328     // private
47329     onDestroy : function(){
47330         if(this.rendered){
47331             
47332             this.tb.items.each(function(item){
47333                 if(item.menu){
47334                     item.menu.removeAll();
47335                     if(item.menu.el){
47336                         item.menu.el.destroy();
47337                     }
47338                 }
47339                 item.destroy();
47340             });
47341              
47342         }
47343     },
47344     onFirstFocus: function() {
47345         // need to do this for all the toolbars..
47346         this.tb.items.each(function(item){
47347            item.enable();
47348         });
47349     },
47350     buildToolbar: function(tlist, nm)
47351     {
47352         var editor = this.editor;
47353         var editorcore = this.editorcore;
47354          // create a new element.
47355         var wdiv = editor.wrap.createChild({
47356                 tag: 'div'
47357             }, editor.wrap.dom.firstChild.nextSibling, true);
47358         
47359        
47360         var tb = new Roo.Toolbar(wdiv);
47361         // add the name..
47362         
47363         tb.add(nm+ ":&nbsp;");
47364         
47365         var styles = [];
47366         for(var i in this.styles) {
47367             styles.push(i);
47368         }
47369         
47370         // styles...
47371         if (styles && styles.length) {
47372             
47373             // this needs a multi-select checkbox...
47374             tb.addField( new Roo.form.ComboBox({
47375                 store: new Roo.data.SimpleStore({
47376                     id : 'val',
47377                     fields: ['val', 'selected'],
47378                     data : [] 
47379                 }),
47380                 name : '-roo-edit-className',
47381                 attrname : 'className',
47382                 displayField: 'val',
47383                 typeAhead: false,
47384                 mode: 'local',
47385                 editable : false,
47386                 triggerAction: 'all',
47387                 emptyText:'Select Style',
47388                 selectOnFocus:true,
47389                 width: 130,
47390                 listeners : {
47391                     'select': function(c, r, i) {
47392                         // initial support only for on class per el..
47393                         tb.selectedNode.className =  r ? r.get('val') : '';
47394                         editorcore.syncValue();
47395                     }
47396                 }
47397     
47398             }));
47399         }
47400         
47401         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47402         var tbops = tbc.options;
47403         
47404         for (var i in tlist) {
47405             
47406             var item = tlist[i];
47407             tb.add(item.title + ":&nbsp;");
47408             
47409             
47410             //optname == used so you can configure the options available..
47411             var opts = item.opts ? item.opts : false;
47412             if (item.optname) {
47413                 opts = tbops[item.optname];
47414            
47415             }
47416             
47417             if (opts) {
47418                 // opts == pulldown..
47419                 tb.addField( new Roo.form.ComboBox({
47420                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47421                         id : 'val',
47422                         fields: ['val', 'display'],
47423                         data : opts  
47424                     }),
47425                     name : '-roo-edit-' + i,
47426                     attrname : i,
47427                     stylename : item.style ? item.style : false,
47428                     displayField: item.displayField ? item.displayField : 'val',
47429                     valueField :  'val',
47430                     typeAhead: false,
47431                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47432                     editable : false,
47433                     triggerAction: 'all',
47434                     emptyText:'Select',
47435                     selectOnFocus:true,
47436                     width: item.width ? item.width  : 130,
47437                     listeners : {
47438                         'select': function(c, r, i) {
47439                             if (c.stylename) {
47440                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47441                                 return;
47442                             }
47443                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47444                         }
47445                     }
47446
47447                 }));
47448                 continue;
47449                     
47450                  
47451                 
47452                 tb.addField( new Roo.form.TextField({
47453                     name: i,
47454                     width: 100,
47455                     //allowBlank:false,
47456                     value: ''
47457                 }));
47458                 continue;
47459             }
47460             tb.addField( new Roo.form.TextField({
47461                 name: '-roo-edit-' + i,
47462                 attrname : i,
47463                 
47464                 width: item.width,
47465                 //allowBlank:true,
47466                 value: '',
47467                 listeners: {
47468                     'change' : function(f, nv, ov) {
47469                         tb.selectedNode.setAttribute(f.attrname, nv);
47470                         editorcore.syncValue();
47471                     }
47472                 }
47473             }));
47474              
47475         }
47476         
47477         var _this = this;
47478         
47479         if(nm == 'BODY'){
47480             tb.addSeparator();
47481         
47482             tb.addButton( {
47483                 text: 'Stylesheets',
47484
47485                 listeners : {
47486                     click : function ()
47487                     {
47488                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47489                     }
47490                 }
47491             });
47492         }
47493         
47494         tb.addFill();
47495         tb.addButton( {
47496             text: 'Remove Tag',
47497     
47498             listeners : {
47499                 click : function ()
47500                 {
47501                     // remove
47502                     // undo does not work.
47503                      
47504                     var sn = tb.selectedNode;
47505                     
47506                     var pn = sn.parentNode;
47507                     
47508                     var stn =  sn.childNodes[0];
47509                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47510                     while (sn.childNodes.length) {
47511                         var node = sn.childNodes[0];
47512                         sn.removeChild(node);
47513                         //Roo.log(node);
47514                         pn.insertBefore(node, sn);
47515                         
47516                     }
47517                     pn.removeChild(sn);
47518                     var range = editorcore.createRange();
47519         
47520                     range.setStart(stn,0);
47521                     range.setEnd(en,0); //????
47522                     //range.selectNode(sel);
47523                     
47524                     
47525                     var selection = editorcore.getSelection();
47526                     selection.removeAllRanges();
47527                     selection.addRange(range);
47528                     
47529                     
47530                     
47531                     //_this.updateToolbar(null, null, pn);
47532                     _this.updateToolbar(null, null, null);
47533                     _this.footDisp.dom.innerHTML = ''; 
47534                 }
47535             }
47536             
47537                     
47538                 
47539             
47540         });
47541         
47542         
47543         tb.el.on('click', function(e){
47544             e.preventDefault(); // what does this do?
47545         });
47546         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47547         tb.el.hide();
47548         tb.name = nm;
47549         // dont need to disable them... as they will get hidden
47550         return tb;
47551          
47552         
47553     },
47554     buildFooter : function()
47555     {
47556         
47557         var fel = this.editor.wrap.createChild();
47558         this.footer = new Roo.Toolbar(fel);
47559         // toolbar has scrolly on left / right?
47560         var footDisp= new Roo.Toolbar.Fill();
47561         var _t = this;
47562         this.footer.add(
47563             {
47564                 text : '&lt;',
47565                 xtype: 'Button',
47566                 handler : function() {
47567                     _t.footDisp.scrollTo('left',0,true)
47568                 }
47569             }
47570         );
47571         this.footer.add( footDisp );
47572         this.footer.add( 
47573             {
47574                 text : '&gt;',
47575                 xtype: 'Button',
47576                 handler : function() {
47577                     // no animation..
47578                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47579                 }
47580             }
47581         );
47582         var fel = Roo.get(footDisp.el);
47583         fel.addClass('x-editor-context');
47584         this.footDispWrap = fel; 
47585         this.footDispWrap.overflow  = 'hidden';
47586         
47587         this.footDisp = fel.createChild();
47588         this.footDispWrap.on('click', this.onContextClick, this)
47589         
47590         
47591     },
47592     onContextClick : function (ev,dom)
47593     {
47594         ev.preventDefault();
47595         var  cn = dom.className;
47596         //Roo.log(cn);
47597         if (!cn.match(/x-ed-loc-/)) {
47598             return;
47599         }
47600         var n = cn.split('-').pop();
47601         var ans = this.footerEls;
47602         var sel = ans[n];
47603         
47604          // pick
47605         var range = this.editorcore.createRange();
47606         
47607         range.selectNodeContents(sel);
47608         //range.selectNode(sel);
47609         
47610         
47611         var selection = this.editorcore.getSelection();
47612         selection.removeAllRanges();
47613         selection.addRange(range);
47614         
47615         
47616         
47617         this.updateToolbar(null, null, sel);
47618         
47619         
47620     }
47621     
47622     
47623     
47624     
47625     
47626 });
47627
47628
47629
47630
47631
47632 /*
47633  * Based on:
47634  * Ext JS Library 1.1.1
47635  * Copyright(c) 2006-2007, Ext JS, LLC.
47636  *
47637  * Originally Released Under LGPL - original licence link has changed is not relivant.
47638  *
47639  * Fork - LGPL
47640  * <script type="text/javascript">
47641  */
47642  
47643 /**
47644  * @class Roo.form.BasicForm
47645  * @extends Roo.util.Observable
47646  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47647  * @constructor
47648  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47649  * @param {Object} config Configuration options
47650  */
47651 Roo.form.BasicForm = function(el, config){
47652     this.allItems = [];
47653     this.childForms = [];
47654     Roo.apply(this, config);
47655     /*
47656      * The Roo.form.Field items in this form.
47657      * @type MixedCollection
47658      */
47659      
47660      
47661     this.items = new Roo.util.MixedCollection(false, function(o){
47662         return o.id || (o.id = Roo.id());
47663     });
47664     this.addEvents({
47665         /**
47666          * @event beforeaction
47667          * Fires before any action is performed. Return false to cancel the action.
47668          * @param {Form} this
47669          * @param {Action} action The action to be performed
47670          */
47671         beforeaction: true,
47672         /**
47673          * @event actionfailed
47674          * Fires when an action fails.
47675          * @param {Form} this
47676          * @param {Action} action The action that failed
47677          */
47678         actionfailed : true,
47679         /**
47680          * @event actioncomplete
47681          * Fires when an action is completed.
47682          * @param {Form} this
47683          * @param {Action} action The action that completed
47684          */
47685         actioncomplete : true
47686     });
47687     if(el){
47688         this.initEl(el);
47689     }
47690     Roo.form.BasicForm.superclass.constructor.call(this);
47691     
47692     Roo.form.BasicForm.popover.apply();
47693 };
47694
47695 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47696     /**
47697      * @cfg {String} method
47698      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47699      */
47700     /**
47701      * @cfg {DataReader} reader
47702      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47703      * This is optional as there is built-in support for processing JSON.
47704      */
47705     /**
47706      * @cfg {DataReader} errorReader
47707      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47708      * This is completely optional as there is built-in support for processing JSON.
47709      */
47710     /**
47711      * @cfg {String} url
47712      * The URL to use for form actions if one isn't supplied in the action options.
47713      */
47714     /**
47715      * @cfg {Boolean} fileUpload
47716      * Set to true if this form is a file upload.
47717      */
47718      
47719     /**
47720      * @cfg {Object} baseParams
47721      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47722      */
47723      /**
47724      
47725     /**
47726      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47727      */
47728     timeout: 30,
47729
47730     // private
47731     activeAction : null,
47732
47733     /**
47734      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47735      * or setValues() data instead of when the form was first created.
47736      */
47737     trackResetOnLoad : false,
47738     
47739     
47740     /**
47741      * childForms - used for multi-tab forms
47742      * @type {Array}
47743      */
47744     childForms : false,
47745     
47746     /**
47747      * allItems - full list of fields.
47748      * @type {Array}
47749      */
47750     allItems : false,
47751     
47752     /**
47753      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47754      * element by passing it or its id or mask the form itself by passing in true.
47755      * @type Mixed
47756      */
47757     waitMsgTarget : false,
47758     
47759     /**
47760      * @type Boolean
47761      */
47762     disableMask : false,
47763     
47764     /**
47765      * @cfg {Boolean} errorMask (true|false) default false
47766      */
47767     errorMask : false,
47768     
47769     /**
47770      * @cfg {Number} maskOffset Default 100
47771      */
47772     maskOffset : 100,
47773
47774     // private
47775     initEl : function(el){
47776         this.el = Roo.get(el);
47777         this.id = this.el.id || Roo.id();
47778         this.el.on('submit', this.onSubmit, this);
47779         this.el.addClass('x-form');
47780     },
47781
47782     // private
47783     onSubmit : function(e){
47784         e.stopEvent();
47785     },
47786
47787     /**
47788      * Returns true if client-side validation on the form is successful.
47789      * @return Boolean
47790      */
47791     isValid : function(){
47792         var valid = true;
47793         var target = false;
47794         this.items.each(function(f){
47795             if(f.validate()){
47796                 return;
47797             }
47798             
47799             valid = false;
47800                 
47801             if(!target && f.el.isVisible(true)){
47802                 target = f;
47803             }
47804         });
47805         
47806         if(this.errorMask && !valid){
47807             Roo.form.BasicForm.popover.mask(this, target);
47808         }
47809         
47810         return valid;
47811     },
47812     /**
47813      * Returns array of invalid form fields.
47814      * @return Array
47815      */
47816     
47817     invalidFields : function()
47818     {
47819         var ret = [];
47820         this.items.each(function(f){
47821             if(f.validate()){
47822                 return;
47823             }
47824             ret.push(f);
47825             
47826         });
47827         
47828         return ret;
47829     },
47830     
47831     
47832     /**
47833      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47834      * @return Boolean
47835      */
47836     isDirty : function(){
47837         var dirty = false;
47838         this.items.each(function(f){
47839            if(f.isDirty()){
47840                dirty = true;
47841                return false;
47842            }
47843         });
47844         return dirty;
47845     },
47846     
47847     /**
47848      * Returns true if any fields in this form have changed since their original load. (New version)
47849      * @return Boolean
47850      */
47851     
47852     hasChanged : function()
47853     {
47854         var dirty = false;
47855         this.items.each(function(f){
47856            if(f.hasChanged()){
47857                dirty = true;
47858                return false;
47859            }
47860         });
47861         return dirty;
47862         
47863     },
47864     /**
47865      * Resets all hasChanged to 'false' -
47866      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47867      * So hasChanged storage is only to be used for this purpose
47868      * @return Boolean
47869      */
47870     resetHasChanged : function()
47871     {
47872         this.items.each(function(f){
47873            f.resetHasChanged();
47874         });
47875         
47876     },
47877     
47878     
47879     /**
47880      * Performs a predefined action (submit or load) or custom actions you define on this form.
47881      * @param {String} actionName The name of the action type
47882      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47883      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47884      * accept other config options):
47885      * <pre>
47886 Property          Type             Description
47887 ----------------  ---------------  ----------------------------------------------------------------------------------
47888 url               String           The url for the action (defaults to the form's url)
47889 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47890 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47891 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47892                                    validate the form on the client (defaults to false)
47893      * </pre>
47894      * @return {BasicForm} this
47895      */
47896     doAction : function(action, options){
47897         if(typeof action == 'string'){
47898             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47899         }
47900         if(this.fireEvent('beforeaction', this, action) !== false){
47901             this.beforeAction(action);
47902             action.run.defer(100, action);
47903         }
47904         return this;
47905     },
47906
47907     /**
47908      * Shortcut to do a submit action.
47909      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47910      * @return {BasicForm} this
47911      */
47912     submit : function(options){
47913         this.doAction('submit', options);
47914         return this;
47915     },
47916
47917     /**
47918      * Shortcut to do a load action.
47919      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47920      * @return {BasicForm} this
47921      */
47922     load : function(options){
47923         this.doAction('load', options);
47924         return this;
47925     },
47926
47927     /**
47928      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47929      * @param {Record} record The record to edit
47930      * @return {BasicForm} this
47931      */
47932     updateRecord : function(record){
47933         record.beginEdit();
47934         var fs = record.fields;
47935         fs.each(function(f){
47936             var field = this.findField(f.name);
47937             if(field){
47938                 record.set(f.name, field.getValue());
47939             }
47940         }, this);
47941         record.endEdit();
47942         return this;
47943     },
47944
47945     /**
47946      * Loads an Roo.data.Record into this form.
47947      * @param {Record} record The record to load
47948      * @return {BasicForm} this
47949      */
47950     loadRecord : function(record){
47951         this.setValues(record.data);
47952         return this;
47953     },
47954
47955     // private
47956     beforeAction : function(action){
47957         var o = action.options;
47958         
47959         if(!this.disableMask) {
47960             if(this.waitMsgTarget === true){
47961                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47962             }else if(this.waitMsgTarget){
47963                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47964                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47965             }else {
47966                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47967             }
47968         }
47969         
47970          
47971     },
47972
47973     // private
47974     afterAction : function(action, success){
47975         this.activeAction = null;
47976         var o = action.options;
47977         
47978         if(!this.disableMask) {
47979             if(this.waitMsgTarget === true){
47980                 this.el.unmask();
47981             }else if(this.waitMsgTarget){
47982                 this.waitMsgTarget.unmask();
47983             }else{
47984                 Roo.MessageBox.updateProgress(1);
47985                 Roo.MessageBox.hide();
47986             }
47987         }
47988         
47989         if(success){
47990             if(o.reset){
47991                 this.reset();
47992             }
47993             Roo.callback(o.success, o.scope, [this, action]);
47994             this.fireEvent('actioncomplete', this, action);
47995             
47996         }else{
47997             
47998             // failure condition..
47999             // we have a scenario where updates need confirming.
48000             // eg. if a locking scenario exists..
48001             // we look for { errors : { needs_confirm : true }} in the response.
48002             if (
48003                 (typeof(action.result) != 'undefined')  &&
48004                 (typeof(action.result.errors) != 'undefined')  &&
48005                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48006            ){
48007                 var _t = this;
48008                 Roo.MessageBox.confirm(
48009                     "Change requires confirmation",
48010                     action.result.errorMsg,
48011                     function(r) {
48012                         if (r != 'yes') {
48013                             return;
48014                         }
48015                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48016                     }
48017                     
48018                 );
48019                 
48020                 
48021                 
48022                 return;
48023             }
48024             
48025             Roo.callback(o.failure, o.scope, [this, action]);
48026             // show an error message if no failed handler is set..
48027             if (!this.hasListener('actionfailed')) {
48028                 Roo.MessageBox.alert("Error",
48029                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48030                         action.result.errorMsg :
48031                         "Saving Failed, please check your entries or try again"
48032                 );
48033             }
48034             
48035             this.fireEvent('actionfailed', this, action);
48036         }
48037         
48038     },
48039
48040     /**
48041      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48042      * @param {String} id The value to search for
48043      * @return Field
48044      */
48045     findField : function(id){
48046         var field = this.items.get(id);
48047         if(!field){
48048             this.items.each(function(f){
48049                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48050                     field = f;
48051                     return false;
48052                 }
48053             });
48054         }
48055         return field || null;
48056     },
48057
48058     /**
48059      * Add a secondary form to this one, 
48060      * Used to provide tabbed forms. One form is primary, with hidden values 
48061      * which mirror the elements from the other forms.
48062      * 
48063      * @param {Roo.form.Form} form to add.
48064      * 
48065      */
48066     addForm : function(form)
48067     {
48068        
48069         if (this.childForms.indexOf(form) > -1) {
48070             // already added..
48071             return;
48072         }
48073         this.childForms.push(form);
48074         var n = '';
48075         Roo.each(form.allItems, function (fe) {
48076             
48077             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48078             if (this.findField(n)) { // already added..
48079                 return;
48080             }
48081             var add = new Roo.form.Hidden({
48082                 name : n
48083             });
48084             add.render(this.el);
48085             
48086             this.add( add );
48087         }, this);
48088         
48089     },
48090     /**
48091      * Mark fields in this form invalid in bulk.
48092      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48093      * @return {BasicForm} this
48094      */
48095     markInvalid : function(errors){
48096         if(errors instanceof Array){
48097             for(var i = 0, len = errors.length; i < len; i++){
48098                 var fieldError = errors[i];
48099                 var f = this.findField(fieldError.id);
48100                 if(f){
48101                     f.markInvalid(fieldError.msg);
48102                 }
48103             }
48104         }else{
48105             var field, id;
48106             for(id in errors){
48107                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48108                     field.markInvalid(errors[id]);
48109                 }
48110             }
48111         }
48112         Roo.each(this.childForms || [], function (f) {
48113             f.markInvalid(errors);
48114         });
48115         
48116         return this;
48117     },
48118
48119     /**
48120      * Set values for fields in this form in bulk.
48121      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48122      * @return {BasicForm} this
48123      */
48124     setValues : function(values){
48125         if(values instanceof Array){ // array of objects
48126             for(var i = 0, len = values.length; i < len; i++){
48127                 var v = values[i];
48128                 var f = this.findField(v.id);
48129                 if(f){
48130                     f.setValue(v.value);
48131                     if(this.trackResetOnLoad){
48132                         f.originalValue = f.getValue();
48133                     }
48134                 }
48135             }
48136         }else{ // object hash
48137             var field, id;
48138             for(id in values){
48139                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48140                     
48141                     if (field.setFromData && 
48142                         field.valueField && 
48143                         field.displayField &&
48144                         // combos' with local stores can 
48145                         // be queried via setValue()
48146                         // to set their value..
48147                         (field.store && !field.store.isLocal)
48148                         ) {
48149                         // it's a combo
48150                         var sd = { };
48151                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48152                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48153                         field.setFromData(sd);
48154                         
48155                     } else {
48156                         field.setValue(values[id]);
48157                     }
48158                     
48159                     
48160                     if(this.trackResetOnLoad){
48161                         field.originalValue = field.getValue();
48162                     }
48163                 }
48164             }
48165         }
48166         this.resetHasChanged();
48167         
48168         
48169         Roo.each(this.childForms || [], function (f) {
48170             f.setValues(values);
48171             f.resetHasChanged();
48172         });
48173                 
48174         return this;
48175     },
48176  
48177     /**
48178      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48179      * they are returned as an array.
48180      * @param {Boolean} asString
48181      * @return {Object}
48182      */
48183     getValues : function(asString){
48184         if (this.childForms) {
48185             // copy values from the child forms
48186             Roo.each(this.childForms, function (f) {
48187                 this.setValues(f.getValues());
48188             }, this);
48189         }
48190         
48191         // use formdata
48192         if (typeof(FormData) != 'undefined' && asString !== true) {
48193             // this relies on a 'recent' version of chrome apparently...
48194             try {
48195                 var fd = (new FormData(this.el.dom)).entries();
48196                 var ret = {};
48197                 var ent = fd.next();
48198                 while (!ent.done) {
48199                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48200                     ent = fd.next();
48201                 };
48202                 return ret;
48203             } catch(e) {
48204                 
48205             }
48206             
48207         }
48208         
48209         
48210         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48211         if(asString === true){
48212             return fs;
48213         }
48214         return Roo.urlDecode(fs);
48215     },
48216     
48217     /**
48218      * Returns the fields in this form as an object with key/value pairs. 
48219      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48220      * @return {Object}
48221      */
48222     getFieldValues : function(with_hidden)
48223     {
48224         if (this.childForms) {
48225             // copy values from the child forms
48226             // should this call getFieldValues - probably not as we do not currently copy
48227             // hidden fields when we generate..
48228             Roo.each(this.childForms, function (f) {
48229                 this.setValues(f.getValues());
48230             }, this);
48231         }
48232         
48233         var ret = {};
48234         this.items.each(function(f){
48235             if (!f.getName()) {
48236                 return;
48237             }
48238             var v = f.getValue();
48239             if (f.inputType =='radio') {
48240                 if (typeof(ret[f.getName()]) == 'undefined') {
48241                     ret[f.getName()] = ''; // empty..
48242                 }
48243                 
48244                 if (!f.el.dom.checked) {
48245                     return;
48246                     
48247                 }
48248                 v = f.el.dom.value;
48249                 
48250             }
48251             
48252             // not sure if this supported any more..
48253             if ((typeof(v) == 'object') && f.getRawValue) {
48254                 v = f.getRawValue() ; // dates..
48255             }
48256             // combo boxes where name != hiddenName...
48257             if (f.name != f.getName()) {
48258                 ret[f.name] = f.getRawValue();
48259             }
48260             ret[f.getName()] = v;
48261         });
48262         
48263         return ret;
48264     },
48265
48266     /**
48267      * Clears all invalid messages in this form.
48268      * @return {BasicForm} this
48269      */
48270     clearInvalid : function(){
48271         this.items.each(function(f){
48272            f.clearInvalid();
48273         });
48274         
48275         Roo.each(this.childForms || [], function (f) {
48276             f.clearInvalid();
48277         });
48278         
48279         
48280         return this;
48281     },
48282
48283     /**
48284      * Resets this form.
48285      * @return {BasicForm} this
48286      */
48287     reset : function(){
48288         this.items.each(function(f){
48289             f.reset();
48290         });
48291         
48292         Roo.each(this.childForms || [], function (f) {
48293             f.reset();
48294         });
48295         this.resetHasChanged();
48296         
48297         return this;
48298     },
48299
48300     /**
48301      * Add Roo.form components to this form.
48302      * @param {Field} field1
48303      * @param {Field} field2 (optional)
48304      * @param {Field} etc (optional)
48305      * @return {BasicForm} this
48306      */
48307     add : function(){
48308         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48309         return this;
48310     },
48311
48312
48313     /**
48314      * Removes a field from the items collection (does NOT remove its markup).
48315      * @param {Field} field
48316      * @return {BasicForm} this
48317      */
48318     remove : function(field){
48319         this.items.remove(field);
48320         return this;
48321     },
48322
48323     /**
48324      * Looks at the fields in this form, checks them for an id attribute,
48325      * and calls applyTo on the existing dom element with that id.
48326      * @return {BasicForm} this
48327      */
48328     render : function(){
48329         this.items.each(function(f){
48330             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48331                 f.applyTo(f.id);
48332             }
48333         });
48334         return this;
48335     },
48336
48337     /**
48338      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48339      * @param {Object} values
48340      * @return {BasicForm} this
48341      */
48342     applyToFields : function(o){
48343         this.items.each(function(f){
48344            Roo.apply(f, o);
48345         });
48346         return this;
48347     },
48348
48349     /**
48350      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48351      * @param {Object} values
48352      * @return {BasicForm} this
48353      */
48354     applyIfToFields : function(o){
48355         this.items.each(function(f){
48356            Roo.applyIf(f, o);
48357         });
48358         return this;
48359     }
48360 });
48361
48362 // back compat
48363 Roo.BasicForm = Roo.form.BasicForm;
48364
48365 Roo.apply(Roo.form.BasicForm, {
48366     
48367     popover : {
48368         
48369         padding : 5,
48370         
48371         isApplied : false,
48372         
48373         isMasked : false,
48374         
48375         form : false,
48376         
48377         target : false,
48378         
48379         intervalID : false,
48380         
48381         maskEl : false,
48382         
48383         apply : function()
48384         {
48385             if(this.isApplied){
48386                 return;
48387             }
48388             
48389             this.maskEl = {
48390                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48391                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48392                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48393                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48394             };
48395             
48396             this.maskEl.top.enableDisplayMode("block");
48397             this.maskEl.left.enableDisplayMode("block");
48398             this.maskEl.bottom.enableDisplayMode("block");
48399             this.maskEl.right.enableDisplayMode("block");
48400             
48401             Roo.get(document.body).on('click', function(){
48402                 this.unmask();
48403             }, this);
48404             
48405             Roo.get(document.body).on('touchstart', function(){
48406                 this.unmask();
48407             }, this);
48408             
48409             this.isApplied = true
48410         },
48411         
48412         mask : function(form, target)
48413         {
48414             this.form = form;
48415             
48416             this.target = target;
48417             
48418             if(!this.form.errorMask || !target.el){
48419                 return;
48420             }
48421             
48422             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48423             
48424             var ot = this.target.el.calcOffsetsTo(scrollable);
48425             
48426             var scrollTo = ot[1] - this.form.maskOffset;
48427             
48428             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48429             
48430             scrollable.scrollTo('top', scrollTo);
48431             
48432             var el = this.target.wrap || this.target.el;
48433             
48434             var box = el.getBox();
48435             
48436             this.maskEl.top.setStyle('position', 'absolute');
48437             this.maskEl.top.setStyle('z-index', 10000);
48438             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48439             this.maskEl.top.setLeft(0);
48440             this.maskEl.top.setTop(0);
48441             this.maskEl.top.show();
48442             
48443             this.maskEl.left.setStyle('position', 'absolute');
48444             this.maskEl.left.setStyle('z-index', 10000);
48445             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48446             this.maskEl.left.setLeft(0);
48447             this.maskEl.left.setTop(box.y - this.padding);
48448             this.maskEl.left.show();
48449
48450             this.maskEl.bottom.setStyle('position', 'absolute');
48451             this.maskEl.bottom.setStyle('z-index', 10000);
48452             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48453             this.maskEl.bottom.setLeft(0);
48454             this.maskEl.bottom.setTop(box.bottom + this.padding);
48455             this.maskEl.bottom.show();
48456
48457             this.maskEl.right.setStyle('position', 'absolute');
48458             this.maskEl.right.setStyle('z-index', 10000);
48459             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48460             this.maskEl.right.setLeft(box.right + this.padding);
48461             this.maskEl.right.setTop(box.y - this.padding);
48462             this.maskEl.right.show();
48463
48464             this.intervalID = window.setInterval(function() {
48465                 Roo.form.BasicForm.popover.unmask();
48466             }, 10000);
48467
48468             window.onwheel = function(){ return false;};
48469             
48470             (function(){ this.isMasked = true; }).defer(500, this);
48471             
48472         },
48473         
48474         unmask : function()
48475         {
48476             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48477                 return;
48478             }
48479             
48480             this.maskEl.top.setStyle('position', 'absolute');
48481             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48482             this.maskEl.top.hide();
48483
48484             this.maskEl.left.setStyle('position', 'absolute');
48485             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48486             this.maskEl.left.hide();
48487
48488             this.maskEl.bottom.setStyle('position', 'absolute');
48489             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48490             this.maskEl.bottom.hide();
48491
48492             this.maskEl.right.setStyle('position', 'absolute');
48493             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48494             this.maskEl.right.hide();
48495             
48496             window.onwheel = function(){ return true;};
48497             
48498             if(this.intervalID){
48499                 window.clearInterval(this.intervalID);
48500                 this.intervalID = false;
48501             }
48502             
48503             this.isMasked = false;
48504             
48505         }
48506         
48507     }
48508     
48509 });/*
48510  * Based on:
48511  * Ext JS Library 1.1.1
48512  * Copyright(c) 2006-2007, Ext JS, LLC.
48513  *
48514  * Originally Released Under LGPL - original licence link has changed is not relivant.
48515  *
48516  * Fork - LGPL
48517  * <script type="text/javascript">
48518  */
48519
48520 /**
48521  * @class Roo.form.Form
48522  * @extends Roo.form.BasicForm
48523  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
48524  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48525  * @constructor
48526  * @param {Object} config Configuration options
48527  */
48528 Roo.form.Form = function(config){
48529     var xitems =  [];
48530     if (config.items) {
48531         xitems = config.items;
48532         delete config.items;
48533     }
48534    
48535     
48536     Roo.form.Form.superclass.constructor.call(this, null, config);
48537     this.url = this.url || this.action;
48538     if(!this.root){
48539         this.root = new Roo.form.Layout(Roo.applyIf({
48540             id: Roo.id()
48541         }, config));
48542     }
48543     this.active = this.root;
48544     /**
48545      * Array of all the buttons that have been added to this form via {@link addButton}
48546      * @type Array
48547      */
48548     this.buttons = [];
48549     this.allItems = [];
48550     this.addEvents({
48551         /**
48552          * @event clientvalidation
48553          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48554          * @param {Form} this
48555          * @param {Boolean} valid true if the form has passed client-side validation
48556          */
48557         clientvalidation: true,
48558         /**
48559          * @event rendered
48560          * Fires when the form is rendered
48561          * @param {Roo.form.Form} form
48562          */
48563         rendered : true
48564     });
48565     
48566     if (this.progressUrl) {
48567             // push a hidden field onto the list of fields..
48568             this.addxtype( {
48569                     xns: Roo.form, 
48570                     xtype : 'Hidden', 
48571                     name : 'UPLOAD_IDENTIFIER' 
48572             });
48573         }
48574         
48575     
48576     Roo.each(xitems, this.addxtype, this);
48577     
48578 };
48579
48580 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48581      /**
48582      * @cfg {Roo.Button} buttons[] buttons at bottom of form
48583      */
48584     
48585     /**
48586      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48587      */
48588     /**
48589      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48590      */
48591     /**
48592      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48593      */
48594     buttonAlign:'center',
48595
48596     /**
48597      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48598      */
48599     minButtonWidth:75,
48600
48601     /**
48602      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48603      * This property cascades to child containers if not set.
48604      */
48605     labelAlign:'left',
48606
48607     /**
48608      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48609      * fires a looping event with that state. This is required to bind buttons to the valid
48610      * state using the config value formBind:true on the button.
48611      */
48612     monitorValid : false,
48613
48614     /**
48615      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48616      */
48617     monitorPoll : 200,
48618     
48619     /**
48620      * @cfg {String} progressUrl - Url to return progress data 
48621      */
48622     
48623     progressUrl : false,
48624     /**
48625      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48626      * sending a formdata with extra parameters - eg uploaded elements.
48627      */
48628     
48629     formData : false,
48630     
48631     /**
48632      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48633      * fields are added and the column is closed. If no fields are passed the column remains open
48634      * until end() is called.
48635      * @param {Object} config The config to pass to the column
48636      * @param {Field} field1 (optional)
48637      * @param {Field} field2 (optional)
48638      * @param {Field} etc (optional)
48639      * @return Column The column container object
48640      */
48641     column : function(c){
48642         var col = new Roo.form.Column(c);
48643         this.start(col);
48644         if(arguments.length > 1){ // duplicate code required because of Opera
48645             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48646             this.end();
48647         }
48648         return col;
48649     },
48650
48651     /**
48652      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48653      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48654      * until end() is called.
48655      * @param {Object} config The config to pass to the fieldset
48656      * @param {Field} field1 (optional)
48657      * @param {Field} field2 (optional)
48658      * @param {Field} etc (optional)
48659      * @return FieldSet The fieldset container object
48660      */
48661     fieldset : function(c){
48662         var fs = new Roo.form.FieldSet(c);
48663         this.start(fs);
48664         if(arguments.length > 1){ // duplicate code required because of Opera
48665             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48666             this.end();
48667         }
48668         return fs;
48669     },
48670
48671     /**
48672      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48673      * fields are added and the container is closed. If no fields are passed the container remains open
48674      * until end() is called.
48675      * @param {Object} config The config to pass to the Layout
48676      * @param {Field} field1 (optional)
48677      * @param {Field} field2 (optional)
48678      * @param {Field} etc (optional)
48679      * @return Layout The container object
48680      */
48681     container : function(c){
48682         var l = new Roo.form.Layout(c);
48683         this.start(l);
48684         if(arguments.length > 1){ // duplicate code required because of Opera
48685             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48686             this.end();
48687         }
48688         return l;
48689     },
48690
48691     /**
48692      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48693      * @param {Object} container A Roo.form.Layout or subclass of Layout
48694      * @return {Form} this
48695      */
48696     start : function(c){
48697         // cascade label info
48698         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48699         this.active.stack.push(c);
48700         c.ownerCt = this.active;
48701         this.active = c;
48702         return this;
48703     },
48704
48705     /**
48706      * Closes the current open container
48707      * @return {Form} this
48708      */
48709     end : function(){
48710         if(this.active == this.root){
48711             return this;
48712         }
48713         this.active = this.active.ownerCt;
48714         return this;
48715     },
48716
48717     /**
48718      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48719      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48720      * as the label of the field.
48721      * @param {Field} field1
48722      * @param {Field} field2 (optional)
48723      * @param {Field} etc. (optional)
48724      * @return {Form} this
48725      */
48726     add : function(){
48727         this.active.stack.push.apply(this.active.stack, arguments);
48728         this.allItems.push.apply(this.allItems,arguments);
48729         var r = [];
48730         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48731             if(a[i].isFormField){
48732                 r.push(a[i]);
48733             }
48734         }
48735         if(r.length > 0){
48736             Roo.form.Form.superclass.add.apply(this, r);
48737         }
48738         return this;
48739     },
48740     
48741
48742     
48743     
48744     
48745      /**
48746      * Find any element that has been added to a form, using it's ID or name
48747      * This can include framesets, columns etc. along with regular fields..
48748      * @param {String} id - id or name to find.
48749      
48750      * @return {Element} e - or false if nothing found.
48751      */
48752     findbyId : function(id)
48753     {
48754         var ret = false;
48755         if (!id) {
48756             return ret;
48757         }
48758         Roo.each(this.allItems, function(f){
48759             if (f.id == id || f.name == id ){
48760                 ret = f;
48761                 return false;
48762             }
48763         });
48764         return ret;
48765     },
48766
48767     
48768     
48769     /**
48770      * Render this form into the passed container. This should only be called once!
48771      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48772      * @return {Form} this
48773      */
48774     render : function(ct)
48775     {
48776         
48777         
48778         
48779         ct = Roo.get(ct);
48780         var o = this.autoCreate || {
48781             tag: 'form',
48782             method : this.method || 'POST',
48783             id : this.id || Roo.id()
48784         };
48785         this.initEl(ct.createChild(o));
48786
48787         this.root.render(this.el);
48788         
48789        
48790              
48791         this.items.each(function(f){
48792             f.render('x-form-el-'+f.id);
48793         });
48794
48795         if(this.buttons.length > 0){
48796             // tables are required to maintain order and for correct IE layout
48797             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48798                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48799                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48800             }}, null, true);
48801             var tr = tb.getElementsByTagName('tr')[0];
48802             for(var i = 0, len = this.buttons.length; i < len; i++) {
48803                 var b = this.buttons[i];
48804                 var td = document.createElement('td');
48805                 td.className = 'x-form-btn-td';
48806                 b.render(tr.appendChild(td));
48807             }
48808         }
48809         if(this.monitorValid){ // initialize after render
48810             this.startMonitoring();
48811         }
48812         this.fireEvent('rendered', this);
48813         return this;
48814     },
48815
48816     /**
48817      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48818      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48819      * object or a valid Roo.DomHelper element config
48820      * @param {Function} handler The function called when the button is clicked
48821      * @param {Object} scope (optional) The scope of the handler function
48822      * @return {Roo.Button}
48823      */
48824     addButton : function(config, handler, scope){
48825         var bc = {
48826             handler: handler,
48827             scope: scope,
48828             minWidth: this.minButtonWidth,
48829             hideParent:true
48830         };
48831         if(typeof config == "string"){
48832             bc.text = config;
48833         }else{
48834             Roo.apply(bc, config);
48835         }
48836         var btn = new Roo.Button(null, bc);
48837         this.buttons.push(btn);
48838         return btn;
48839     },
48840
48841      /**
48842      * Adds a series of form elements (using the xtype property as the factory method.
48843      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48844      * @param {Object} config 
48845      */
48846     
48847     addxtype : function()
48848     {
48849         var ar = Array.prototype.slice.call(arguments, 0);
48850         var ret = false;
48851         for(var i = 0; i < ar.length; i++) {
48852             if (!ar[i]) {
48853                 continue; // skip -- if this happends something invalid got sent, we 
48854                 // should ignore it, as basically that interface element will not show up
48855                 // and that should be pretty obvious!!
48856             }
48857             
48858             if (Roo.form[ar[i].xtype]) {
48859                 ar[i].form = this;
48860                 var fe = Roo.factory(ar[i], Roo.form);
48861                 if (!ret) {
48862                     ret = fe;
48863                 }
48864                 fe.form = this;
48865                 if (fe.store) {
48866                     fe.store.form = this;
48867                 }
48868                 if (fe.isLayout) {  
48869                          
48870                     this.start(fe);
48871                     this.allItems.push(fe);
48872                     if (fe.items && fe.addxtype) {
48873                         fe.addxtype.apply(fe, fe.items);
48874                         delete fe.items;
48875                     }
48876                      this.end();
48877                     continue;
48878                 }
48879                 
48880                 
48881                  
48882                 this.add(fe);
48883               //  console.log('adding ' + ar[i].xtype);
48884             }
48885             if (ar[i].xtype == 'Button') {  
48886                 //console.log('adding button');
48887                 //console.log(ar[i]);
48888                 this.addButton(ar[i]);
48889                 this.allItems.push(fe);
48890                 continue;
48891             }
48892             
48893             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48894                 alert('end is not supported on xtype any more, use items');
48895             //    this.end();
48896             //    //console.log('adding end');
48897             }
48898             
48899         }
48900         return ret;
48901     },
48902     
48903     /**
48904      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48905      * option "monitorValid"
48906      */
48907     startMonitoring : function(){
48908         if(!this.bound){
48909             this.bound = true;
48910             Roo.TaskMgr.start({
48911                 run : this.bindHandler,
48912                 interval : this.monitorPoll || 200,
48913                 scope: this
48914             });
48915         }
48916     },
48917
48918     /**
48919      * Stops monitoring of the valid state of this form
48920      */
48921     stopMonitoring : function(){
48922         this.bound = false;
48923     },
48924
48925     // private
48926     bindHandler : function(){
48927         if(!this.bound){
48928             return false; // stops binding
48929         }
48930         var valid = true;
48931         this.items.each(function(f){
48932             if(!f.isValid(true)){
48933                 valid = false;
48934                 return false;
48935             }
48936         });
48937         for(var i = 0, len = this.buttons.length; i < len; i++){
48938             var btn = this.buttons[i];
48939             if(btn.formBind === true && btn.disabled === valid){
48940                 btn.setDisabled(!valid);
48941             }
48942         }
48943         this.fireEvent('clientvalidation', this, valid);
48944     }
48945     
48946     
48947     
48948     
48949     
48950     
48951     
48952     
48953 });
48954
48955
48956 // back compat
48957 Roo.Form = Roo.form.Form;
48958 /*
48959  * Based on:
48960  * Ext JS Library 1.1.1
48961  * Copyright(c) 2006-2007, Ext JS, LLC.
48962  *
48963  * Originally Released Under LGPL - original licence link has changed is not relivant.
48964  *
48965  * Fork - LGPL
48966  * <script type="text/javascript">
48967  */
48968
48969 // as we use this in bootstrap.
48970 Roo.namespace('Roo.form');
48971  /**
48972  * @class Roo.form.Action
48973  * Internal Class used to handle form actions
48974  * @constructor
48975  * @param {Roo.form.BasicForm} el The form element or its id
48976  * @param {Object} config Configuration options
48977  */
48978
48979  
48980  
48981 // define the action interface
48982 Roo.form.Action = function(form, options){
48983     this.form = form;
48984     this.options = options || {};
48985 };
48986 /**
48987  * Client Validation Failed
48988  * @const 
48989  */
48990 Roo.form.Action.CLIENT_INVALID = 'client';
48991 /**
48992  * Server Validation Failed
48993  * @const 
48994  */
48995 Roo.form.Action.SERVER_INVALID = 'server';
48996  /**
48997  * Connect to Server Failed
48998  * @const 
48999  */
49000 Roo.form.Action.CONNECT_FAILURE = 'connect';
49001 /**
49002  * Reading Data from Server Failed
49003  * @const 
49004  */
49005 Roo.form.Action.LOAD_FAILURE = 'load';
49006
49007 Roo.form.Action.prototype = {
49008     type : 'default',
49009     failureType : undefined,
49010     response : undefined,
49011     result : undefined,
49012
49013     // interface method
49014     run : function(options){
49015
49016     },
49017
49018     // interface method
49019     success : function(response){
49020
49021     },
49022
49023     // interface method
49024     handleResponse : function(response){
49025
49026     },
49027
49028     // default connection failure
49029     failure : function(response){
49030         
49031         this.response = response;
49032         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49033         this.form.afterAction(this, false);
49034     },
49035
49036     processResponse : function(response){
49037         this.response = response;
49038         if(!response.responseText){
49039             return true;
49040         }
49041         this.result = this.handleResponse(response);
49042         return this.result;
49043     },
49044
49045     // utility functions used internally
49046     getUrl : function(appendParams){
49047         var url = this.options.url || this.form.url || this.form.el.dom.action;
49048         if(appendParams){
49049             var p = this.getParams();
49050             if(p){
49051                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49052             }
49053         }
49054         return url;
49055     },
49056
49057     getMethod : function(){
49058         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49059     },
49060
49061     getParams : function(){
49062         var bp = this.form.baseParams;
49063         var p = this.options.params;
49064         if(p){
49065             if(typeof p == "object"){
49066                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49067             }else if(typeof p == 'string' && bp){
49068                 p += '&' + Roo.urlEncode(bp);
49069             }
49070         }else if(bp){
49071             p = Roo.urlEncode(bp);
49072         }
49073         return p;
49074     },
49075
49076     createCallback : function(){
49077         return {
49078             success: this.success,
49079             failure: this.failure,
49080             scope: this,
49081             timeout: (this.form.timeout*1000),
49082             upload: this.form.fileUpload ? this.success : undefined
49083         };
49084     }
49085 };
49086
49087 Roo.form.Action.Submit = function(form, options){
49088     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49089 };
49090
49091 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49092     type : 'submit',
49093
49094     haveProgress : false,
49095     uploadComplete : false,
49096     
49097     // uploadProgress indicator.
49098     uploadProgress : function()
49099     {
49100         if (!this.form.progressUrl) {
49101             return;
49102         }
49103         
49104         if (!this.haveProgress) {
49105             Roo.MessageBox.progress("Uploading", "Uploading");
49106         }
49107         if (this.uploadComplete) {
49108            Roo.MessageBox.hide();
49109            return;
49110         }
49111         
49112         this.haveProgress = true;
49113    
49114         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49115         
49116         var c = new Roo.data.Connection();
49117         c.request({
49118             url : this.form.progressUrl,
49119             params: {
49120                 id : uid
49121             },
49122             method: 'GET',
49123             success : function(req){
49124                //console.log(data);
49125                 var rdata = false;
49126                 var edata;
49127                 try  {
49128                    rdata = Roo.decode(req.responseText)
49129                 } catch (e) {
49130                     Roo.log("Invalid data from server..");
49131                     Roo.log(edata);
49132                     return;
49133                 }
49134                 if (!rdata || !rdata.success) {
49135                     Roo.log(rdata);
49136                     Roo.MessageBox.alert(Roo.encode(rdata));
49137                     return;
49138                 }
49139                 var data = rdata.data;
49140                 
49141                 if (this.uploadComplete) {
49142                    Roo.MessageBox.hide();
49143                    return;
49144                 }
49145                    
49146                 if (data){
49147                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49148                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49149                     );
49150                 }
49151                 this.uploadProgress.defer(2000,this);
49152             },
49153        
49154             failure: function(data) {
49155                 Roo.log('progress url failed ');
49156                 Roo.log(data);
49157             },
49158             scope : this
49159         });
49160            
49161     },
49162     
49163     
49164     run : function()
49165     {
49166         // run get Values on the form, so it syncs any secondary forms.
49167         this.form.getValues();
49168         
49169         var o = this.options;
49170         var method = this.getMethod();
49171         var isPost = method == 'POST';
49172         if(o.clientValidation === false || this.form.isValid()){
49173             
49174             if (this.form.progressUrl) {
49175                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49176                     (new Date() * 1) + '' + Math.random());
49177                     
49178             } 
49179             
49180             
49181             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49182                 form:this.form.el.dom,
49183                 url:this.getUrl(!isPost),
49184                 method: method,
49185                 params:isPost ? this.getParams() : null,
49186                 isUpload: this.form.fileUpload,
49187                 formData : this.form.formData
49188             }));
49189             
49190             this.uploadProgress();
49191
49192         }else if (o.clientValidation !== false){ // client validation failed
49193             this.failureType = Roo.form.Action.CLIENT_INVALID;
49194             this.form.afterAction(this, false);
49195         }
49196     },
49197
49198     success : function(response)
49199     {
49200         this.uploadComplete= true;
49201         if (this.haveProgress) {
49202             Roo.MessageBox.hide();
49203         }
49204         
49205         
49206         var result = this.processResponse(response);
49207         if(result === true || result.success){
49208             this.form.afterAction(this, true);
49209             return;
49210         }
49211         if(result.errors){
49212             this.form.markInvalid(result.errors);
49213             this.failureType = Roo.form.Action.SERVER_INVALID;
49214         }
49215         this.form.afterAction(this, false);
49216     },
49217     failure : function(response)
49218     {
49219         this.uploadComplete= true;
49220         if (this.haveProgress) {
49221             Roo.MessageBox.hide();
49222         }
49223         
49224         this.response = response;
49225         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49226         this.form.afterAction(this, false);
49227     },
49228     
49229     handleResponse : function(response){
49230         if(this.form.errorReader){
49231             var rs = this.form.errorReader.read(response);
49232             var errors = [];
49233             if(rs.records){
49234                 for(var i = 0, len = rs.records.length; i < len; i++) {
49235                     var r = rs.records[i];
49236                     errors[i] = r.data;
49237                 }
49238             }
49239             if(errors.length < 1){
49240                 errors = null;
49241             }
49242             return {
49243                 success : rs.success,
49244                 errors : errors
49245             };
49246         }
49247         var ret = false;
49248         try {
49249             ret = Roo.decode(response.responseText);
49250         } catch (e) {
49251             ret = {
49252                 success: false,
49253                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49254                 errors : []
49255             };
49256         }
49257         return ret;
49258         
49259     }
49260 });
49261
49262
49263 Roo.form.Action.Load = function(form, options){
49264     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49265     this.reader = this.form.reader;
49266 };
49267
49268 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49269     type : 'load',
49270
49271     run : function(){
49272         
49273         Roo.Ajax.request(Roo.apply(
49274                 this.createCallback(), {
49275                     method:this.getMethod(),
49276                     url:this.getUrl(false),
49277                     params:this.getParams()
49278         }));
49279     },
49280
49281     success : function(response){
49282         
49283         var result = this.processResponse(response);
49284         if(result === true || !result.success || !result.data){
49285             this.failureType = Roo.form.Action.LOAD_FAILURE;
49286             this.form.afterAction(this, false);
49287             return;
49288         }
49289         this.form.clearInvalid();
49290         this.form.setValues(result.data);
49291         this.form.afterAction(this, true);
49292     },
49293
49294     handleResponse : function(response){
49295         if(this.form.reader){
49296             var rs = this.form.reader.read(response);
49297             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49298             return {
49299                 success : rs.success,
49300                 data : data
49301             };
49302         }
49303         return Roo.decode(response.responseText);
49304     }
49305 });
49306
49307 Roo.form.Action.ACTION_TYPES = {
49308     'load' : Roo.form.Action.Load,
49309     'submit' : Roo.form.Action.Submit
49310 };/*
49311  * Based on:
49312  * Ext JS Library 1.1.1
49313  * Copyright(c) 2006-2007, Ext JS, LLC.
49314  *
49315  * Originally Released Under LGPL - original licence link has changed is not relivant.
49316  *
49317  * Fork - LGPL
49318  * <script type="text/javascript">
49319  */
49320  
49321 /**
49322  * @class Roo.form.Layout
49323  * @extends Roo.Component
49324  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49325  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49326  * @constructor
49327  * @param {Object} config Configuration options
49328  */
49329 Roo.form.Layout = function(config){
49330     var xitems = [];
49331     if (config.items) {
49332         xitems = config.items;
49333         delete config.items;
49334     }
49335     Roo.form.Layout.superclass.constructor.call(this, config);
49336     this.stack = [];
49337     Roo.each(xitems, this.addxtype, this);
49338      
49339 };
49340
49341 Roo.extend(Roo.form.Layout, Roo.Component, {
49342     /**
49343      * @cfg {String/Object} autoCreate
49344      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49345      */
49346     /**
49347      * @cfg {String/Object/Function} style
49348      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49349      * a function which returns such a specification.
49350      */
49351     /**
49352      * @cfg {String} labelAlign
49353      * Valid values are "left," "top" and "right" (defaults to "left")
49354      */
49355     /**
49356      * @cfg {Number} labelWidth
49357      * Fixed width in pixels of all field labels (defaults to undefined)
49358      */
49359     /**
49360      * @cfg {Boolean} clear
49361      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49362      */
49363     clear : true,
49364     /**
49365      * @cfg {String} labelSeparator
49366      * The separator to use after field labels (defaults to ':')
49367      */
49368     labelSeparator : ':',
49369     /**
49370      * @cfg {Boolean} hideLabels
49371      * True to suppress the display of field labels in this layout (defaults to false)
49372      */
49373     hideLabels : false,
49374
49375     // private
49376     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49377     
49378     isLayout : true,
49379     
49380     // private
49381     onRender : function(ct, position){
49382         if(this.el){ // from markup
49383             this.el = Roo.get(this.el);
49384         }else {  // generate
49385             var cfg = this.getAutoCreate();
49386             this.el = ct.createChild(cfg, position);
49387         }
49388         if(this.style){
49389             this.el.applyStyles(this.style);
49390         }
49391         if(this.labelAlign){
49392             this.el.addClass('x-form-label-'+this.labelAlign);
49393         }
49394         if(this.hideLabels){
49395             this.labelStyle = "display:none";
49396             this.elementStyle = "padding-left:0;";
49397         }else{
49398             if(typeof this.labelWidth == 'number'){
49399                 this.labelStyle = "width:"+this.labelWidth+"px;";
49400                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49401             }
49402             if(this.labelAlign == 'top'){
49403                 this.labelStyle = "width:auto;";
49404                 this.elementStyle = "padding-left:0;";
49405             }
49406         }
49407         var stack = this.stack;
49408         var slen = stack.length;
49409         if(slen > 0){
49410             if(!this.fieldTpl){
49411                 var t = new Roo.Template(
49412                     '<div class="x-form-item {5}">',
49413                         '<label for="{0}" style="{2}">{1}{4}</label>',
49414                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49415                         '</div>',
49416                     '</div><div class="x-form-clear-left"></div>'
49417                 );
49418                 t.disableFormats = true;
49419                 t.compile();
49420                 Roo.form.Layout.prototype.fieldTpl = t;
49421             }
49422             for(var i = 0; i < slen; i++) {
49423                 if(stack[i].isFormField){
49424                     this.renderField(stack[i]);
49425                 }else{
49426                     this.renderComponent(stack[i]);
49427                 }
49428             }
49429         }
49430         if(this.clear){
49431             this.el.createChild({cls:'x-form-clear'});
49432         }
49433     },
49434
49435     // private
49436     renderField : function(f){
49437         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49438                f.id, //0
49439                f.fieldLabel, //1
49440                f.labelStyle||this.labelStyle||'', //2
49441                this.elementStyle||'', //3
49442                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49443                f.itemCls||this.itemCls||''  //5
49444        ], true).getPrevSibling());
49445     },
49446
49447     // private
49448     renderComponent : function(c){
49449         c.render(c.isLayout ? this.el : this.el.createChild());    
49450     },
49451     /**
49452      * Adds a object form elements (using the xtype property as the factory method.)
49453      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49454      * @param {Object} config 
49455      */
49456     addxtype : function(o)
49457     {
49458         // create the lement.
49459         o.form = this.form;
49460         var fe = Roo.factory(o, Roo.form);
49461         this.form.allItems.push(fe);
49462         this.stack.push(fe);
49463         
49464         if (fe.isFormField) {
49465             this.form.items.add(fe);
49466         }
49467          
49468         return fe;
49469     }
49470 });
49471
49472 /**
49473  * @class Roo.form.Column
49474  * @extends Roo.form.Layout
49475  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49476  * @constructor
49477  * @param {Object} config Configuration options
49478  */
49479 Roo.form.Column = function(config){
49480     Roo.form.Column.superclass.constructor.call(this, config);
49481 };
49482
49483 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49484     /**
49485      * @cfg {Number/String} width
49486      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49487      */
49488     /**
49489      * @cfg {String/Object} autoCreate
49490      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49491      */
49492
49493     // private
49494     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49495
49496     // private
49497     onRender : function(ct, position){
49498         Roo.form.Column.superclass.onRender.call(this, ct, position);
49499         if(this.width){
49500             this.el.setWidth(this.width);
49501         }
49502     }
49503 });
49504
49505
49506 /**
49507  * @class Roo.form.Row
49508  * @extends Roo.form.Layout
49509  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49510  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49511  * @constructor
49512  * @param {Object} config Configuration options
49513  */
49514
49515  
49516 Roo.form.Row = function(config){
49517     Roo.form.Row.superclass.constructor.call(this, config);
49518 };
49519  
49520 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49521       /**
49522      * @cfg {Number/String} width
49523      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49524      */
49525     /**
49526      * @cfg {Number/String} height
49527      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49528      */
49529     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49530     
49531     padWidth : 20,
49532     // private
49533     onRender : function(ct, position){
49534         //console.log('row render');
49535         if(!this.rowTpl){
49536             var t = new Roo.Template(
49537                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49538                     '<label for="{0}" style="{2}">{1}{4}</label>',
49539                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49540                     '</div>',
49541                 '</div>'
49542             );
49543             t.disableFormats = true;
49544             t.compile();
49545             Roo.form.Layout.prototype.rowTpl = t;
49546         }
49547         this.fieldTpl = this.rowTpl;
49548         
49549         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49550         var labelWidth = 100;
49551         
49552         if ((this.labelAlign != 'top')) {
49553             if (typeof this.labelWidth == 'number') {
49554                 labelWidth = this.labelWidth
49555             }
49556             this.padWidth =  20 + labelWidth;
49557             
49558         }
49559         
49560         Roo.form.Column.superclass.onRender.call(this, ct, position);
49561         if(this.width){
49562             this.el.setWidth(this.width);
49563         }
49564         if(this.height){
49565             this.el.setHeight(this.height);
49566         }
49567     },
49568     
49569     // private
49570     renderField : function(f){
49571         f.fieldEl = this.fieldTpl.append(this.el, [
49572                f.id, f.fieldLabel,
49573                f.labelStyle||this.labelStyle||'',
49574                this.elementStyle||'',
49575                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49576                f.itemCls||this.itemCls||'',
49577                f.width ? f.width + this.padWidth : 160 + this.padWidth
49578        ],true);
49579     }
49580 });
49581  
49582
49583 /**
49584  * @class Roo.form.FieldSet
49585  * @extends Roo.form.Layout
49586  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49587  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49588  * @constructor
49589  * @param {Object} config Configuration options
49590  */
49591 Roo.form.FieldSet = function(config){
49592     Roo.form.FieldSet.superclass.constructor.call(this, config);
49593 };
49594
49595 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49596     /**
49597      * @cfg {String} legend
49598      * The text to display as the legend for the FieldSet (defaults to '')
49599      */
49600     /**
49601      * @cfg {String/Object} autoCreate
49602      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49603      */
49604
49605     // private
49606     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49607
49608     // private
49609     onRender : function(ct, position){
49610         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49611         if(this.legend){
49612             this.setLegend(this.legend);
49613         }
49614     },
49615
49616     // private
49617     setLegend : function(text){
49618         if(this.rendered){
49619             this.el.child('legend').update(text);
49620         }
49621     }
49622 });/*
49623  * Based on:
49624  * Ext JS Library 1.1.1
49625  * Copyright(c) 2006-2007, Ext JS, LLC.
49626  *
49627  * Originally Released Under LGPL - original licence link has changed is not relivant.
49628  *
49629  * Fork - LGPL
49630  * <script type="text/javascript">
49631  */
49632 /**
49633  * @class Roo.form.VTypes
49634  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49635  * @singleton
49636  */
49637 Roo.form.VTypes = function(){
49638     // closure these in so they are only created once.
49639     var alpha = /^[a-zA-Z_]+$/;
49640     var alphanum = /^[a-zA-Z0-9_]+$/;
49641     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49642     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49643
49644     // All these messages and functions are configurable
49645     return {
49646         /**
49647          * The function used to validate email addresses
49648          * @param {String} value The email address
49649          */
49650         'email' : function(v){
49651             return email.test(v);
49652         },
49653         /**
49654          * The error text to display when the email validation function returns false
49655          * @type String
49656          */
49657         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49658         /**
49659          * The keystroke filter mask to be applied on email input
49660          * @type RegExp
49661          */
49662         'emailMask' : /[a-z0-9_\.\-@]/i,
49663
49664         /**
49665          * The function used to validate URLs
49666          * @param {String} value The URL
49667          */
49668         'url' : function(v){
49669             return url.test(v);
49670         },
49671         /**
49672          * The error text to display when the url validation function returns false
49673          * @type String
49674          */
49675         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49676         
49677         /**
49678          * The function used to validate alpha values
49679          * @param {String} value The value
49680          */
49681         'alpha' : function(v){
49682             return alpha.test(v);
49683         },
49684         /**
49685          * The error text to display when the alpha validation function returns false
49686          * @type String
49687          */
49688         'alphaText' : 'This field should only contain letters and _',
49689         /**
49690          * The keystroke filter mask to be applied on alpha input
49691          * @type RegExp
49692          */
49693         'alphaMask' : /[a-z_]/i,
49694
49695         /**
49696          * The function used to validate alphanumeric values
49697          * @param {String} value The value
49698          */
49699         'alphanum' : function(v){
49700             return alphanum.test(v);
49701         },
49702         /**
49703          * The error text to display when the alphanumeric validation function returns false
49704          * @type String
49705          */
49706         'alphanumText' : 'This field should only contain letters, numbers and _',
49707         /**
49708          * The keystroke filter mask to be applied on alphanumeric input
49709          * @type RegExp
49710          */
49711         'alphanumMask' : /[a-z0-9_]/i
49712     };
49713 }();//<script type="text/javascript">
49714
49715 /**
49716  * @class Roo.form.FCKeditor
49717  * @extends Roo.form.TextArea
49718  * Wrapper around the FCKEditor http://www.fckeditor.net
49719  * @constructor
49720  * Creates a new FCKeditor
49721  * @param {Object} config Configuration options
49722  */
49723 Roo.form.FCKeditor = function(config){
49724     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49725     this.addEvents({
49726          /**
49727          * @event editorinit
49728          * Fired when the editor is initialized - you can add extra handlers here..
49729          * @param {FCKeditor} this
49730          * @param {Object} the FCK object.
49731          */
49732         editorinit : true
49733     });
49734     
49735     
49736 };
49737 Roo.form.FCKeditor.editors = { };
49738 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49739 {
49740     //defaultAutoCreate : {
49741     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49742     //},
49743     // private
49744     /**
49745      * @cfg {Object} fck options - see fck manual for details.
49746      */
49747     fckconfig : false,
49748     
49749     /**
49750      * @cfg {Object} fck toolbar set (Basic or Default)
49751      */
49752     toolbarSet : 'Basic',
49753     /**
49754      * @cfg {Object} fck BasePath
49755      */ 
49756     basePath : '/fckeditor/',
49757     
49758     
49759     frame : false,
49760     
49761     value : '',
49762     
49763    
49764     onRender : function(ct, position)
49765     {
49766         if(!this.el){
49767             this.defaultAutoCreate = {
49768                 tag: "textarea",
49769                 style:"width:300px;height:60px;",
49770                 autocomplete: "new-password"
49771             };
49772         }
49773         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49774         /*
49775         if(this.grow){
49776             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49777             if(this.preventScrollbars){
49778                 this.el.setStyle("overflow", "hidden");
49779             }
49780             this.el.setHeight(this.growMin);
49781         }
49782         */
49783         //console.log('onrender' + this.getId() );
49784         Roo.form.FCKeditor.editors[this.getId()] = this;
49785          
49786
49787         this.replaceTextarea() ;
49788         
49789     },
49790     
49791     getEditor : function() {
49792         return this.fckEditor;
49793     },
49794     /**
49795      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49796      * @param {Mixed} value The value to set
49797      */
49798     
49799     
49800     setValue : function(value)
49801     {
49802         //console.log('setValue: ' + value);
49803         
49804         if(typeof(value) == 'undefined') { // not sure why this is happending...
49805             return;
49806         }
49807         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49808         
49809         //if(!this.el || !this.getEditor()) {
49810         //    this.value = value;
49811             //this.setValue.defer(100,this,[value]);    
49812         //    return;
49813         //} 
49814         
49815         if(!this.getEditor()) {
49816             return;
49817         }
49818         
49819         this.getEditor().SetData(value);
49820         
49821         //
49822
49823     },
49824
49825     /**
49826      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49827      * @return {Mixed} value The field value
49828      */
49829     getValue : function()
49830     {
49831         
49832         if (this.frame && this.frame.dom.style.display == 'none') {
49833             return Roo.form.FCKeditor.superclass.getValue.call(this);
49834         }
49835         
49836         if(!this.el || !this.getEditor()) {
49837            
49838            // this.getValue.defer(100,this); 
49839             return this.value;
49840         }
49841        
49842         
49843         var value=this.getEditor().GetData();
49844         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49845         return Roo.form.FCKeditor.superclass.getValue.call(this);
49846         
49847
49848     },
49849
49850     /**
49851      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49852      * @return {Mixed} value The field value
49853      */
49854     getRawValue : function()
49855     {
49856         if (this.frame && this.frame.dom.style.display == 'none') {
49857             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49858         }
49859         
49860         if(!this.el || !this.getEditor()) {
49861             //this.getRawValue.defer(100,this); 
49862             return this.value;
49863             return;
49864         }
49865         
49866         
49867         
49868         var value=this.getEditor().GetData();
49869         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49870         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49871          
49872     },
49873     
49874     setSize : function(w,h) {
49875         
49876         
49877         
49878         //if (this.frame && this.frame.dom.style.display == 'none') {
49879         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49880         //    return;
49881         //}
49882         //if(!this.el || !this.getEditor()) {
49883         //    this.setSize.defer(100,this, [w,h]); 
49884         //    return;
49885         //}
49886         
49887         
49888         
49889         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49890         
49891         this.frame.dom.setAttribute('width', w);
49892         this.frame.dom.setAttribute('height', h);
49893         this.frame.setSize(w,h);
49894         
49895     },
49896     
49897     toggleSourceEdit : function(value) {
49898         
49899       
49900          
49901         this.el.dom.style.display = value ? '' : 'none';
49902         this.frame.dom.style.display = value ?  'none' : '';
49903         
49904     },
49905     
49906     
49907     focus: function(tag)
49908     {
49909         if (this.frame.dom.style.display == 'none') {
49910             return Roo.form.FCKeditor.superclass.focus.call(this);
49911         }
49912         if(!this.el || !this.getEditor()) {
49913             this.focus.defer(100,this, [tag]); 
49914             return;
49915         }
49916         
49917         
49918         
49919         
49920         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49921         this.getEditor().Focus();
49922         if (tgs.length) {
49923             if (!this.getEditor().Selection.GetSelection()) {
49924                 this.focus.defer(100,this, [tag]); 
49925                 return;
49926             }
49927             
49928             
49929             var r = this.getEditor().EditorDocument.createRange();
49930             r.setStart(tgs[0],0);
49931             r.setEnd(tgs[0],0);
49932             this.getEditor().Selection.GetSelection().removeAllRanges();
49933             this.getEditor().Selection.GetSelection().addRange(r);
49934             this.getEditor().Focus();
49935         }
49936         
49937     },
49938     
49939     
49940     
49941     replaceTextarea : function()
49942     {
49943         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49944             return ;
49945         }
49946         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49947         //{
49948             // We must check the elements firstly using the Id and then the name.
49949         var oTextarea = document.getElementById( this.getId() );
49950         
49951         var colElementsByName = document.getElementsByName( this.getId() ) ;
49952          
49953         oTextarea.style.display = 'none' ;
49954
49955         if ( oTextarea.tabIndex ) {            
49956             this.TabIndex = oTextarea.tabIndex ;
49957         }
49958         
49959         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49960         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49961         this.frame = Roo.get(this.getId() + '___Frame')
49962     },
49963     
49964     _getConfigHtml : function()
49965     {
49966         var sConfig = '' ;
49967
49968         for ( var o in this.fckconfig ) {
49969             sConfig += sConfig.length > 0  ? '&amp;' : '';
49970             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49971         }
49972
49973         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49974     },
49975     
49976     
49977     _getIFrameHtml : function()
49978     {
49979         var sFile = 'fckeditor.html' ;
49980         /* no idea what this is about..
49981         try
49982         {
49983             if ( (/fcksource=true/i).test( window.top.location.search ) )
49984                 sFile = 'fckeditor.original.html' ;
49985         }
49986         catch (e) { 
49987         */
49988
49989         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49990         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49991         
49992         
49993         var html = '<iframe id="' + this.getId() +
49994             '___Frame" src="' + sLink +
49995             '" width="' + this.width +
49996             '" height="' + this.height + '"' +
49997             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49998             ' frameborder="0" scrolling="no"></iframe>' ;
49999
50000         return html ;
50001     },
50002     
50003     _insertHtmlBefore : function( html, element )
50004     {
50005         if ( element.insertAdjacentHTML )       {
50006             // IE
50007             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50008         } else { // Gecko
50009             var oRange = document.createRange() ;
50010             oRange.setStartBefore( element ) ;
50011             var oFragment = oRange.createContextualFragment( html );
50012             element.parentNode.insertBefore( oFragment, element ) ;
50013         }
50014     }
50015     
50016     
50017   
50018     
50019     
50020     
50021     
50022
50023 });
50024
50025 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50026
50027 function FCKeditor_OnComplete(editorInstance){
50028     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50029     f.fckEditor = editorInstance;
50030     //console.log("loaded");
50031     f.fireEvent('editorinit', f, editorInstance);
50032
50033   
50034
50035  
50036
50037
50038
50039
50040
50041
50042
50043
50044
50045
50046
50047
50048
50049
50050
50051 //<script type="text/javascript">
50052 /**
50053  * @class Roo.form.GridField
50054  * @extends Roo.form.Field
50055  * Embed a grid (or editable grid into a form)
50056  * STATUS ALPHA
50057  * 
50058  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50059  * it needs 
50060  * xgrid.store = Roo.data.Store
50061  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50062  * xgrid.store.reader = Roo.data.JsonReader 
50063  * 
50064  * 
50065  * @constructor
50066  * Creates a new GridField
50067  * @param {Object} config Configuration options
50068  */
50069 Roo.form.GridField = function(config){
50070     Roo.form.GridField.superclass.constructor.call(this, config);
50071      
50072 };
50073
50074 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50075     /**
50076      * @cfg {Number} width  - used to restrict width of grid..
50077      */
50078     width : 100,
50079     /**
50080      * @cfg {Number} height - used to restrict height of grid..
50081      */
50082     height : 50,
50083      /**
50084      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50085          * 
50086          *}
50087      */
50088     xgrid : false, 
50089     /**
50090      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50091      * {tag: "input", type: "checkbox", autocomplete: "off"})
50092      */
50093    // defaultAutoCreate : { tag: 'div' },
50094     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50095     /**
50096      * @cfg {String} addTitle Text to include for adding a title.
50097      */
50098     addTitle : false,
50099     //
50100     onResize : function(){
50101         Roo.form.Field.superclass.onResize.apply(this, arguments);
50102     },
50103
50104     initEvents : function(){
50105         // Roo.form.Checkbox.superclass.initEvents.call(this);
50106         // has no events...
50107        
50108     },
50109
50110
50111     getResizeEl : function(){
50112         return this.wrap;
50113     },
50114
50115     getPositionEl : function(){
50116         return this.wrap;
50117     },
50118
50119     // private
50120     onRender : function(ct, position){
50121         
50122         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50123         var style = this.style;
50124         delete this.style;
50125         
50126         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50127         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50128         this.viewEl = this.wrap.createChild({ tag: 'div' });
50129         if (style) {
50130             this.viewEl.applyStyles(style);
50131         }
50132         if (this.width) {
50133             this.viewEl.setWidth(this.width);
50134         }
50135         if (this.height) {
50136             this.viewEl.setHeight(this.height);
50137         }
50138         //if(this.inputValue !== undefined){
50139         //this.setValue(this.value);
50140         
50141         
50142         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50143         
50144         
50145         this.grid.render();
50146         this.grid.getDataSource().on('remove', this.refreshValue, this);
50147         this.grid.getDataSource().on('update', this.refreshValue, this);
50148         this.grid.on('afteredit', this.refreshValue, this);
50149  
50150     },
50151      
50152     
50153     /**
50154      * Sets the value of the item. 
50155      * @param {String} either an object  or a string..
50156      */
50157     setValue : function(v){
50158         //this.value = v;
50159         v = v || []; // empty set..
50160         // this does not seem smart - it really only affects memoryproxy grids..
50161         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50162             var ds = this.grid.getDataSource();
50163             // assumes a json reader..
50164             var data = {}
50165             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50166             ds.loadData( data);
50167         }
50168         // clear selection so it does not get stale.
50169         if (this.grid.sm) { 
50170             this.grid.sm.clearSelections();
50171         }
50172         
50173         Roo.form.GridField.superclass.setValue.call(this, v);
50174         this.refreshValue();
50175         // should load data in the grid really....
50176     },
50177     
50178     // private
50179     refreshValue: function() {
50180          var val = [];
50181         this.grid.getDataSource().each(function(r) {
50182             val.push(r.data);
50183         });
50184         this.el.dom.value = Roo.encode(val);
50185     }
50186     
50187      
50188     
50189     
50190 });/*
50191  * Based on:
50192  * Ext JS Library 1.1.1
50193  * Copyright(c) 2006-2007, Ext JS, LLC.
50194  *
50195  * Originally Released Under LGPL - original licence link has changed is not relivant.
50196  *
50197  * Fork - LGPL
50198  * <script type="text/javascript">
50199  */
50200 /**
50201  * @class Roo.form.DisplayField
50202  * @extends Roo.form.Field
50203  * A generic Field to display non-editable data.
50204  * @cfg {Boolean} closable (true|false) default false
50205  * @constructor
50206  * Creates a new Display Field item.
50207  * @param {Object} config Configuration options
50208  */
50209 Roo.form.DisplayField = function(config){
50210     Roo.form.DisplayField.superclass.constructor.call(this, config);
50211     
50212     this.addEvents({
50213         /**
50214          * @event close
50215          * Fires after the click the close btn
50216              * @param {Roo.form.DisplayField} this
50217              */
50218         close : true
50219     });
50220 };
50221
50222 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50223     inputType:      'hidden',
50224     allowBlank:     true,
50225     readOnly:         true,
50226     
50227  
50228     /**
50229      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50230      */
50231     focusClass : undefined,
50232     /**
50233      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50234      */
50235     fieldClass: 'x-form-field',
50236     
50237      /**
50238      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50239      */
50240     valueRenderer: undefined,
50241     
50242     width: 100,
50243     /**
50244      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50245      * {tag: "input", type: "checkbox", autocomplete: "off"})
50246      */
50247      
50248  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50249  
50250     closable : false,
50251     
50252     onResize : function(){
50253         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50254         
50255     },
50256
50257     initEvents : function(){
50258         // Roo.form.Checkbox.superclass.initEvents.call(this);
50259         // has no events...
50260         
50261         if(this.closable){
50262             this.closeEl.on('click', this.onClose, this);
50263         }
50264        
50265     },
50266
50267
50268     getResizeEl : function(){
50269         return this.wrap;
50270     },
50271
50272     getPositionEl : function(){
50273         return this.wrap;
50274     },
50275
50276     // private
50277     onRender : function(ct, position){
50278         
50279         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50280         //if(this.inputValue !== undefined){
50281         this.wrap = this.el.wrap();
50282         
50283         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50284         
50285         if(this.closable){
50286             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50287         }
50288         
50289         if (this.bodyStyle) {
50290             this.viewEl.applyStyles(this.bodyStyle);
50291         }
50292         //this.viewEl.setStyle('padding', '2px');
50293         
50294         this.setValue(this.value);
50295         
50296     },
50297 /*
50298     // private
50299     initValue : Roo.emptyFn,
50300
50301   */
50302
50303         // private
50304     onClick : function(){
50305         
50306     },
50307
50308     /**
50309      * Sets the checked state of the checkbox.
50310      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50311      */
50312     setValue : function(v){
50313         this.value = v;
50314         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50315         // this might be called before we have a dom element..
50316         if (!this.viewEl) {
50317             return;
50318         }
50319         this.viewEl.dom.innerHTML = html;
50320         Roo.form.DisplayField.superclass.setValue.call(this, v);
50321
50322     },
50323     
50324     onClose : function(e)
50325     {
50326         e.preventDefault();
50327         
50328         this.fireEvent('close', this);
50329     }
50330 });/*
50331  * 
50332  * Licence- LGPL
50333  * 
50334  */
50335
50336 /**
50337  * @class Roo.form.DayPicker
50338  * @extends Roo.form.Field
50339  * A Day picker show [M] [T] [W] ....
50340  * @constructor
50341  * Creates a new Day Picker
50342  * @param {Object} config Configuration options
50343  */
50344 Roo.form.DayPicker= function(config){
50345     Roo.form.DayPicker.superclass.constructor.call(this, config);
50346      
50347 };
50348
50349 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50350     /**
50351      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50352      */
50353     focusClass : undefined,
50354     /**
50355      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50356      */
50357     fieldClass: "x-form-field",
50358    
50359     /**
50360      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50361      * {tag: "input", type: "checkbox", autocomplete: "off"})
50362      */
50363     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50364     
50365    
50366     actionMode : 'viewEl', 
50367     //
50368     // private
50369  
50370     inputType : 'hidden',
50371     
50372      
50373     inputElement: false, // real input element?
50374     basedOn: false, // ????
50375     
50376     isFormField: true, // not sure where this is needed!!!!
50377
50378     onResize : function(){
50379         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50380         if(!this.boxLabel){
50381             this.el.alignTo(this.wrap, 'c-c');
50382         }
50383     },
50384
50385     initEvents : function(){
50386         Roo.form.Checkbox.superclass.initEvents.call(this);
50387         this.el.on("click", this.onClick,  this);
50388         this.el.on("change", this.onClick,  this);
50389     },
50390
50391
50392     getResizeEl : function(){
50393         return this.wrap;
50394     },
50395
50396     getPositionEl : function(){
50397         return this.wrap;
50398     },
50399
50400     
50401     // private
50402     onRender : function(ct, position){
50403         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50404        
50405         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50406         
50407         var r1 = '<table><tr>';
50408         var r2 = '<tr class="x-form-daypick-icons">';
50409         for (var i=0; i < 7; i++) {
50410             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50411             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50412         }
50413         
50414         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50415         viewEl.select('img').on('click', this.onClick, this);
50416         this.viewEl = viewEl;   
50417         
50418         
50419         // this will not work on Chrome!!!
50420         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50421         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50422         
50423         
50424           
50425
50426     },
50427
50428     // private
50429     initValue : Roo.emptyFn,
50430
50431     /**
50432      * Returns the checked state of the checkbox.
50433      * @return {Boolean} True if checked, else false
50434      */
50435     getValue : function(){
50436         return this.el.dom.value;
50437         
50438     },
50439
50440         // private
50441     onClick : function(e){ 
50442         //this.setChecked(!this.checked);
50443         Roo.get(e.target).toggleClass('x-menu-item-checked');
50444         this.refreshValue();
50445         //if(this.el.dom.checked != this.checked){
50446         //    this.setValue(this.el.dom.checked);
50447        // }
50448     },
50449     
50450     // private
50451     refreshValue : function()
50452     {
50453         var val = '';
50454         this.viewEl.select('img',true).each(function(e,i,n)  {
50455             val += e.is(".x-menu-item-checked") ? String(n) : '';
50456         });
50457         this.setValue(val, true);
50458     },
50459
50460     /**
50461      * Sets the checked state of the checkbox.
50462      * On is always based on a string comparison between inputValue and the param.
50463      * @param {Boolean/String} value - the value to set 
50464      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50465      */
50466     setValue : function(v,suppressEvent){
50467         if (!this.el.dom) {
50468             return;
50469         }
50470         var old = this.el.dom.value ;
50471         this.el.dom.value = v;
50472         if (suppressEvent) {
50473             return ;
50474         }
50475          
50476         // update display..
50477         this.viewEl.select('img',true).each(function(e,i,n)  {
50478             
50479             var on = e.is(".x-menu-item-checked");
50480             var newv = v.indexOf(String(n)) > -1;
50481             if (on != newv) {
50482                 e.toggleClass('x-menu-item-checked');
50483             }
50484             
50485         });
50486         
50487         
50488         this.fireEvent('change', this, v, old);
50489         
50490         
50491     },
50492    
50493     // handle setting of hidden value by some other method!!?!?
50494     setFromHidden: function()
50495     {
50496         if(!this.el){
50497             return;
50498         }
50499         //console.log("SET FROM HIDDEN");
50500         //alert('setFrom hidden');
50501         this.setValue(this.el.dom.value);
50502     },
50503     
50504     onDestroy : function()
50505     {
50506         if(this.viewEl){
50507             Roo.get(this.viewEl).remove();
50508         }
50509          
50510         Roo.form.DayPicker.superclass.onDestroy.call(this);
50511     }
50512
50513 });/*
50514  * RooJS Library 1.1.1
50515  * Copyright(c) 2008-2011  Alan Knowles
50516  *
50517  * License - LGPL
50518  */
50519  
50520
50521 /**
50522  * @class Roo.form.ComboCheck
50523  * @extends Roo.form.ComboBox
50524  * A combobox for multiple select items.
50525  *
50526  * FIXME - could do with a reset button..
50527  * 
50528  * @constructor
50529  * Create a new ComboCheck
50530  * @param {Object} config Configuration options
50531  */
50532 Roo.form.ComboCheck = function(config){
50533     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50534     // should verify some data...
50535     // like
50536     // hiddenName = required..
50537     // displayField = required
50538     // valudField == required
50539     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50540     var _t = this;
50541     Roo.each(req, function(e) {
50542         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50543             throw "Roo.form.ComboCheck : missing value for: " + e;
50544         }
50545     });
50546     
50547     
50548 };
50549
50550 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50551      
50552      
50553     editable : false,
50554      
50555     selectedClass: 'x-menu-item-checked', 
50556     
50557     // private
50558     onRender : function(ct, position){
50559         var _t = this;
50560         
50561         
50562         
50563         if(!this.tpl){
50564             var cls = 'x-combo-list';
50565
50566             
50567             this.tpl =  new Roo.Template({
50568                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50569                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50570                    '<span>{' + this.displayField + '}</span>' +
50571                     '</div>' 
50572                 
50573             });
50574         }
50575  
50576         
50577         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50578         this.view.singleSelect = false;
50579         this.view.multiSelect = true;
50580         this.view.toggleSelect = true;
50581         this.pageTb.add(new Roo.Toolbar.Fill(), {
50582             
50583             text: 'Done',
50584             handler: function()
50585             {
50586                 _t.collapse();
50587             }
50588         });
50589     },
50590     
50591     onViewOver : function(e, t){
50592         // do nothing...
50593         return;
50594         
50595     },
50596     
50597     onViewClick : function(doFocus,index){
50598         return;
50599         
50600     },
50601     select: function () {
50602         //Roo.log("SELECT CALLED");
50603     },
50604      
50605     selectByValue : function(xv, scrollIntoView){
50606         var ar = this.getValueArray();
50607         var sels = [];
50608         
50609         Roo.each(ar, function(v) {
50610             if(v === undefined || v === null){
50611                 return;
50612             }
50613             var r = this.findRecord(this.valueField, v);
50614             if(r){
50615                 sels.push(this.store.indexOf(r))
50616                 
50617             }
50618         },this);
50619         this.view.select(sels);
50620         return false;
50621     },
50622     
50623     
50624     
50625     onSelect : function(record, index){
50626        // Roo.log("onselect Called");
50627        // this is only called by the clear button now..
50628         this.view.clearSelections();
50629         this.setValue('[]');
50630         if (this.value != this.valueBefore) {
50631             this.fireEvent('change', this, this.value, this.valueBefore);
50632             this.valueBefore = this.value;
50633         }
50634     },
50635     getValueArray : function()
50636     {
50637         var ar = [] ;
50638         
50639         try {
50640             //Roo.log(this.value);
50641             if (typeof(this.value) == 'undefined') {
50642                 return [];
50643             }
50644             var ar = Roo.decode(this.value);
50645             return  ar instanceof Array ? ar : []; //?? valid?
50646             
50647         } catch(e) {
50648             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50649             return [];
50650         }
50651          
50652     },
50653     expand : function ()
50654     {
50655         
50656         Roo.form.ComboCheck.superclass.expand.call(this);
50657         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50658         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50659         
50660
50661     },
50662     
50663     collapse : function(){
50664         Roo.form.ComboCheck.superclass.collapse.call(this);
50665         var sl = this.view.getSelectedIndexes();
50666         var st = this.store;
50667         var nv = [];
50668         var tv = [];
50669         var r;
50670         Roo.each(sl, function(i) {
50671             r = st.getAt(i);
50672             nv.push(r.get(this.valueField));
50673         },this);
50674         this.setValue(Roo.encode(nv));
50675         if (this.value != this.valueBefore) {
50676
50677             this.fireEvent('change', this, this.value, this.valueBefore);
50678             this.valueBefore = this.value;
50679         }
50680         
50681     },
50682     
50683     setValue : function(v){
50684         // Roo.log(v);
50685         this.value = v;
50686         
50687         var vals = this.getValueArray();
50688         var tv = [];
50689         Roo.each(vals, function(k) {
50690             var r = this.findRecord(this.valueField, k);
50691             if(r){
50692                 tv.push(r.data[this.displayField]);
50693             }else if(this.valueNotFoundText !== undefined){
50694                 tv.push( this.valueNotFoundText );
50695             }
50696         },this);
50697        // Roo.log(tv);
50698         
50699         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50700         this.hiddenField.value = v;
50701         this.value = v;
50702     }
50703     
50704 });/*
50705  * Based on:
50706  * Ext JS Library 1.1.1
50707  * Copyright(c) 2006-2007, Ext JS, LLC.
50708  *
50709  * Originally Released Under LGPL - original licence link has changed is not relivant.
50710  *
50711  * Fork - LGPL
50712  * <script type="text/javascript">
50713  */
50714  
50715 /**
50716  * @class Roo.form.Signature
50717  * @extends Roo.form.Field
50718  * Signature field.  
50719  * @constructor
50720  * 
50721  * @param {Object} config Configuration options
50722  */
50723
50724 Roo.form.Signature = function(config){
50725     Roo.form.Signature.superclass.constructor.call(this, config);
50726     
50727     this.addEvents({// not in used??
50728          /**
50729          * @event confirm
50730          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50731              * @param {Roo.form.Signature} combo This combo box
50732              */
50733         'confirm' : true,
50734         /**
50735          * @event reset
50736          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50737              * @param {Roo.form.ComboBox} combo This combo box
50738              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50739              */
50740         'reset' : true
50741     });
50742 };
50743
50744 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50745     /**
50746      * @cfg {Object} labels Label to use when rendering a form.
50747      * defaults to 
50748      * labels : { 
50749      *      clear : "Clear",
50750      *      confirm : "Confirm"
50751      *  }
50752      */
50753     labels : { 
50754         clear : "Clear",
50755         confirm : "Confirm"
50756     },
50757     /**
50758      * @cfg {Number} width The signature panel width (defaults to 300)
50759      */
50760     width: 300,
50761     /**
50762      * @cfg {Number} height The signature panel height (defaults to 100)
50763      */
50764     height : 100,
50765     /**
50766      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50767      */
50768     allowBlank : false,
50769     
50770     //private
50771     // {Object} signPanel The signature SVG panel element (defaults to {})
50772     signPanel : {},
50773     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50774     isMouseDown : false,
50775     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50776     isConfirmed : false,
50777     // {String} signatureTmp SVG mapping string (defaults to empty string)
50778     signatureTmp : '',
50779     
50780     
50781     defaultAutoCreate : { // modified by initCompnoent..
50782         tag: "input",
50783         type:"hidden"
50784     },
50785
50786     // private
50787     onRender : function(ct, position){
50788         
50789         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50790         
50791         this.wrap = this.el.wrap({
50792             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50793         });
50794         
50795         this.createToolbar(this);
50796         this.signPanel = this.wrap.createChild({
50797                 tag: 'div',
50798                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50799             }, this.el
50800         );
50801             
50802         this.svgID = Roo.id();
50803         this.svgEl = this.signPanel.createChild({
50804               xmlns : 'http://www.w3.org/2000/svg',
50805               tag : 'svg',
50806               id : this.svgID + "-svg",
50807               width: this.width,
50808               height: this.height,
50809               viewBox: '0 0 '+this.width+' '+this.height,
50810               cn : [
50811                 {
50812                     tag: "rect",
50813                     id: this.svgID + "-svg-r",
50814                     width: this.width,
50815                     height: this.height,
50816                     fill: "#ffa"
50817                 },
50818                 {
50819                     tag: "line",
50820                     id: this.svgID + "-svg-l",
50821                     x1: "0", // start
50822                     y1: (this.height*0.8), // start set the line in 80% of height
50823                     x2: this.width, // end
50824                     y2: (this.height*0.8), // end set the line in 80% of height
50825                     'stroke': "#666",
50826                     'stroke-width': "1",
50827                     'stroke-dasharray': "3",
50828                     'shape-rendering': "crispEdges",
50829                     'pointer-events': "none"
50830                 },
50831                 {
50832                     tag: "path",
50833                     id: this.svgID + "-svg-p",
50834                     'stroke': "navy",
50835                     'stroke-width': "3",
50836                     'fill': "none",
50837                     'pointer-events': 'none'
50838                 }
50839               ]
50840         });
50841         this.createSVG();
50842         this.svgBox = this.svgEl.dom.getScreenCTM();
50843     },
50844     createSVG : function(){ 
50845         var svg = this.signPanel;
50846         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50847         var t = this;
50848
50849         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50850         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50851         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50852         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50853         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50854         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50855         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50856         
50857     },
50858     isTouchEvent : function(e){
50859         return e.type.match(/^touch/);
50860     },
50861     getCoords : function (e) {
50862         var pt    = this.svgEl.dom.createSVGPoint();
50863         pt.x = e.clientX; 
50864         pt.y = e.clientY;
50865         if (this.isTouchEvent(e)) {
50866             pt.x =  e.targetTouches[0].clientX;
50867             pt.y = e.targetTouches[0].clientY;
50868         }
50869         var a = this.svgEl.dom.getScreenCTM();
50870         var b = a.inverse();
50871         var mx = pt.matrixTransform(b);
50872         return mx.x + ',' + mx.y;
50873     },
50874     //mouse event headler 
50875     down : function (e) {
50876         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50877         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50878         
50879         this.isMouseDown = true;
50880         
50881         e.preventDefault();
50882     },
50883     move : function (e) {
50884         if (this.isMouseDown) {
50885             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50886             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50887         }
50888         
50889         e.preventDefault();
50890     },
50891     up : function (e) {
50892         this.isMouseDown = false;
50893         var sp = this.signatureTmp.split(' ');
50894         
50895         if(sp.length > 1){
50896             if(!sp[sp.length-2].match(/^L/)){
50897                 sp.pop();
50898                 sp.pop();
50899                 sp.push("");
50900                 this.signatureTmp = sp.join(" ");
50901             }
50902         }
50903         if(this.getValue() != this.signatureTmp){
50904             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50905             this.isConfirmed = false;
50906         }
50907         e.preventDefault();
50908     },
50909     
50910     /**
50911      * Protected method that will not generally be called directly. It
50912      * is called when the editor creates its toolbar. Override this method if you need to
50913      * add custom toolbar buttons.
50914      * @param {HtmlEditor} editor
50915      */
50916     createToolbar : function(editor){
50917          function btn(id, toggle, handler){
50918             var xid = fid + '-'+ id ;
50919             return {
50920                 id : xid,
50921                 cmd : id,
50922                 cls : 'x-btn-icon x-edit-'+id,
50923                 enableToggle:toggle !== false,
50924                 scope: editor, // was editor...
50925                 handler:handler||editor.relayBtnCmd,
50926                 clickEvent:'mousedown',
50927                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50928                 tabIndex:-1
50929             };
50930         }
50931         
50932         
50933         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50934         this.tb = tb;
50935         this.tb.add(
50936            {
50937                 cls : ' x-signature-btn x-signature-'+id,
50938                 scope: editor, // was editor...
50939                 handler: this.reset,
50940                 clickEvent:'mousedown',
50941                 text: this.labels.clear
50942             },
50943             {
50944                  xtype : 'Fill',
50945                  xns: Roo.Toolbar
50946             }, 
50947             {
50948                 cls : '  x-signature-btn x-signature-'+id,
50949                 scope: editor, // was editor...
50950                 handler: this.confirmHandler,
50951                 clickEvent:'mousedown',
50952                 text: this.labels.confirm
50953             }
50954         );
50955     
50956     },
50957     //public
50958     /**
50959      * when user is clicked confirm then show this image.....
50960      * 
50961      * @return {String} Image Data URI
50962      */
50963     getImageDataURI : function(){
50964         var svg = this.svgEl.dom.parentNode.innerHTML;
50965         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50966         return src; 
50967     },
50968     /**
50969      * 
50970      * @return {Boolean} this.isConfirmed
50971      */
50972     getConfirmed : function(){
50973         return this.isConfirmed;
50974     },
50975     /**
50976      * 
50977      * @return {Number} this.width
50978      */
50979     getWidth : function(){
50980         return this.width;
50981     },
50982     /**
50983      * 
50984      * @return {Number} this.height
50985      */
50986     getHeight : function(){
50987         return this.height;
50988     },
50989     // private
50990     getSignature : function(){
50991         return this.signatureTmp;
50992     },
50993     // private
50994     reset : function(){
50995         this.signatureTmp = '';
50996         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50997         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50998         this.isConfirmed = false;
50999         Roo.form.Signature.superclass.reset.call(this);
51000     },
51001     setSignature : function(s){
51002         this.signatureTmp = s;
51003         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51004         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51005         this.setValue(s);
51006         this.isConfirmed = false;
51007         Roo.form.Signature.superclass.reset.call(this);
51008     }, 
51009     test : function(){
51010 //        Roo.log(this.signPanel.dom.contentWindow.up())
51011     },
51012     //private
51013     setConfirmed : function(){
51014         
51015         
51016         
51017 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51018     },
51019     // private
51020     confirmHandler : function(){
51021         if(!this.getSignature()){
51022             return;
51023         }
51024         
51025         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51026         this.setValue(this.getSignature());
51027         this.isConfirmed = true;
51028         
51029         this.fireEvent('confirm', this);
51030     },
51031     // private
51032     // Subclasses should provide the validation implementation by overriding this
51033     validateValue : function(value){
51034         if(this.allowBlank){
51035             return true;
51036         }
51037         
51038         if(this.isConfirmed){
51039             return true;
51040         }
51041         return false;
51042     }
51043 });/*
51044  * Based on:
51045  * Ext JS Library 1.1.1
51046  * Copyright(c) 2006-2007, Ext JS, LLC.
51047  *
51048  * Originally Released Under LGPL - original licence link has changed is not relivant.
51049  *
51050  * Fork - LGPL
51051  * <script type="text/javascript">
51052  */
51053  
51054
51055 /**
51056  * @class Roo.form.ComboBox
51057  * @extends Roo.form.TriggerField
51058  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51059  * @constructor
51060  * Create a new ComboBox.
51061  * @param {Object} config Configuration options
51062  */
51063 Roo.form.Select = function(config){
51064     Roo.form.Select.superclass.constructor.call(this, config);
51065      
51066 };
51067
51068 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51069     /**
51070      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51071      */
51072     /**
51073      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51074      * rendering into an Roo.Editor, defaults to false)
51075      */
51076     /**
51077      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51078      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51079      */
51080     /**
51081      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51082      */
51083     /**
51084      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51085      * the dropdown list (defaults to undefined, with no header element)
51086      */
51087
51088      /**
51089      * @cfg {String/Roo.Template} tpl The template to use to render the output
51090      */
51091      
51092     // private
51093     defaultAutoCreate : {tag: "select"  },
51094     /**
51095      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51096      */
51097     listWidth: undefined,
51098     /**
51099      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51100      * mode = 'remote' or 'text' if mode = 'local')
51101      */
51102     displayField: undefined,
51103     /**
51104      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51105      * mode = 'remote' or 'value' if mode = 'local'). 
51106      * Note: use of a valueField requires the user make a selection
51107      * in order for a value to be mapped.
51108      */
51109     valueField: undefined,
51110     
51111     
51112     /**
51113      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51114      * field's data value (defaults to the underlying DOM element's name)
51115      */
51116     hiddenName: undefined,
51117     /**
51118      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51119      */
51120     listClass: '',
51121     /**
51122      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51123      */
51124     selectedClass: 'x-combo-selected',
51125     /**
51126      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51127      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51128      * which displays a downward arrow icon).
51129      */
51130     triggerClass : 'x-form-arrow-trigger',
51131     /**
51132      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51133      */
51134     shadow:'sides',
51135     /**
51136      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51137      * anchor positions (defaults to 'tl-bl')
51138      */
51139     listAlign: 'tl-bl?',
51140     /**
51141      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51142      */
51143     maxHeight: 300,
51144     /**
51145      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51146      * query specified by the allQuery config option (defaults to 'query')
51147      */
51148     triggerAction: 'query',
51149     /**
51150      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51151      * (defaults to 4, does not apply if editable = false)
51152      */
51153     minChars : 4,
51154     /**
51155      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51156      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51157      */
51158     typeAhead: false,
51159     /**
51160      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51161      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51162      */
51163     queryDelay: 500,
51164     /**
51165      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51166      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51167      */
51168     pageSize: 0,
51169     /**
51170      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51171      * when editable = true (defaults to false)
51172      */
51173     selectOnFocus:false,
51174     /**
51175      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51176      */
51177     queryParam: 'query',
51178     /**
51179      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51180      * when mode = 'remote' (defaults to 'Loading...')
51181      */
51182     loadingText: 'Loading...',
51183     /**
51184      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51185      */
51186     resizable: false,
51187     /**
51188      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51189      */
51190     handleHeight : 8,
51191     /**
51192      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51193      * traditional select (defaults to true)
51194      */
51195     editable: true,
51196     /**
51197      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51198      */
51199     allQuery: '',
51200     /**
51201      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51202      */
51203     mode: 'remote',
51204     /**
51205      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51206      * listWidth has a higher value)
51207      */
51208     minListWidth : 70,
51209     /**
51210      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51211      * allow the user to set arbitrary text into the field (defaults to false)
51212      */
51213     forceSelection:false,
51214     /**
51215      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51216      * if typeAhead = true (defaults to 250)
51217      */
51218     typeAheadDelay : 250,
51219     /**
51220      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51221      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51222      */
51223     valueNotFoundText : undefined,
51224     
51225     /**
51226      * @cfg {String} defaultValue The value displayed after loading the store.
51227      */
51228     defaultValue: '',
51229     
51230     /**
51231      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51232      */
51233     blockFocus : false,
51234     
51235     /**
51236      * @cfg {Boolean} disableClear Disable showing of clear button.
51237      */
51238     disableClear : false,
51239     /**
51240      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51241      */
51242     alwaysQuery : false,
51243     
51244     //private
51245     addicon : false,
51246     editicon: false,
51247     
51248     // element that contains real text value.. (when hidden is used..)
51249      
51250     // private
51251     onRender : function(ct, position){
51252         Roo.form.Field.prototype.onRender.call(this, ct, position);
51253         
51254         if(this.store){
51255             this.store.on('beforeload', this.onBeforeLoad, this);
51256             this.store.on('load', this.onLoad, this);
51257             this.store.on('loadexception', this.onLoadException, this);
51258             this.store.load({});
51259         }
51260         
51261         
51262         
51263     },
51264
51265     // private
51266     initEvents : function(){
51267         //Roo.form.ComboBox.superclass.initEvents.call(this);
51268  
51269     },
51270
51271     onDestroy : function(){
51272        
51273         if(this.store){
51274             this.store.un('beforeload', this.onBeforeLoad, this);
51275             this.store.un('load', this.onLoad, this);
51276             this.store.un('loadexception', this.onLoadException, this);
51277         }
51278         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51279     },
51280
51281     // private
51282     fireKey : function(e){
51283         if(e.isNavKeyPress() && !this.list.isVisible()){
51284             this.fireEvent("specialkey", this, e);
51285         }
51286     },
51287
51288     // private
51289     onResize: function(w, h){
51290         
51291         return; 
51292     
51293         
51294     },
51295
51296     /**
51297      * Allow or prevent the user from directly editing the field text.  If false is passed,
51298      * the user will only be able to select from the items defined in the dropdown list.  This method
51299      * is the runtime equivalent of setting the 'editable' config option at config time.
51300      * @param {Boolean} value True to allow the user to directly edit the field text
51301      */
51302     setEditable : function(value){
51303          
51304     },
51305
51306     // private
51307     onBeforeLoad : function(){
51308         
51309         Roo.log("Select before load");
51310         return;
51311     
51312         this.innerList.update(this.loadingText ?
51313                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51314         //this.restrictHeight();
51315         this.selectedIndex = -1;
51316     },
51317
51318     // private
51319     onLoad : function(){
51320
51321     
51322         var dom = this.el.dom;
51323         dom.innerHTML = '';
51324          var od = dom.ownerDocument;
51325          
51326         if (this.emptyText) {
51327             var op = od.createElement('option');
51328             op.setAttribute('value', '');
51329             op.innerHTML = String.format('{0}', this.emptyText);
51330             dom.appendChild(op);
51331         }
51332         if(this.store.getCount() > 0){
51333            
51334             var vf = this.valueField;
51335             var df = this.displayField;
51336             this.store.data.each(function(r) {
51337                 // which colmsn to use... testing - cdoe / title..
51338                 var op = od.createElement('option');
51339                 op.setAttribute('value', r.data[vf]);
51340                 op.innerHTML = String.format('{0}', r.data[df]);
51341                 dom.appendChild(op);
51342             });
51343             if (typeof(this.defaultValue != 'undefined')) {
51344                 this.setValue(this.defaultValue);
51345             }
51346             
51347              
51348         }else{
51349             //this.onEmptyResults();
51350         }
51351         //this.el.focus();
51352     },
51353     // private
51354     onLoadException : function()
51355     {
51356         dom.innerHTML = '';
51357             
51358         Roo.log("Select on load exception");
51359         return;
51360     
51361         this.collapse();
51362         Roo.log(this.store.reader.jsonData);
51363         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51364             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51365         }
51366         
51367         
51368     },
51369     // private
51370     onTypeAhead : function(){
51371          
51372     },
51373
51374     // private
51375     onSelect : function(record, index){
51376         Roo.log('on select?');
51377         return;
51378         if(this.fireEvent('beforeselect', this, record, index) !== false){
51379             this.setFromData(index > -1 ? record.data : false);
51380             this.collapse();
51381             this.fireEvent('select', this, record, index);
51382         }
51383     },
51384
51385     /**
51386      * Returns the currently selected field value or empty string if no value is set.
51387      * @return {String} value The selected value
51388      */
51389     getValue : function(){
51390         var dom = this.el.dom;
51391         this.value = dom.options[dom.selectedIndex].value;
51392         return this.value;
51393         
51394     },
51395
51396     /**
51397      * Clears any text/value currently set in the field
51398      */
51399     clearValue : function(){
51400         this.value = '';
51401         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51402         
51403     },
51404
51405     /**
51406      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51407      * will be displayed in the field.  If the value does not match the data value of an existing item,
51408      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51409      * Otherwise the field will be blank (although the value will still be set).
51410      * @param {String} value The value to match
51411      */
51412     setValue : function(v){
51413         var d = this.el.dom;
51414         for (var i =0; i < d.options.length;i++) {
51415             if (v == d.options[i].value) {
51416                 d.selectedIndex = i;
51417                 this.value = v;
51418                 return;
51419             }
51420         }
51421         this.clearValue();
51422     },
51423     /**
51424      * @property {Object} the last set data for the element
51425      */
51426     
51427     lastData : false,
51428     /**
51429      * Sets the value of the field based on a object which is related to the record format for the store.
51430      * @param {Object} value the value to set as. or false on reset?
51431      */
51432     setFromData : function(o){
51433         Roo.log('setfrom data?');
51434          
51435         
51436         
51437     },
51438     // private
51439     reset : function(){
51440         this.clearValue();
51441     },
51442     // private
51443     findRecord : function(prop, value){
51444         
51445         return false;
51446     
51447         var record;
51448         if(this.store.getCount() > 0){
51449             this.store.each(function(r){
51450                 if(r.data[prop] == value){
51451                     record = r;
51452                     return false;
51453                 }
51454                 return true;
51455             });
51456         }
51457         return record;
51458     },
51459     
51460     getName: function()
51461     {
51462         // returns hidden if it's set..
51463         if (!this.rendered) {return ''};
51464         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51465         
51466     },
51467      
51468
51469     
51470
51471     // private
51472     onEmptyResults : function(){
51473         Roo.log('empty results');
51474         //this.collapse();
51475     },
51476
51477     /**
51478      * Returns true if the dropdown list is expanded, else false.
51479      */
51480     isExpanded : function(){
51481         return false;
51482     },
51483
51484     /**
51485      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51486      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51487      * @param {String} value The data value of the item to select
51488      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51489      * selected item if it is not currently in view (defaults to true)
51490      * @return {Boolean} True if the value matched an item in the list, else false
51491      */
51492     selectByValue : function(v, scrollIntoView){
51493         Roo.log('select By Value');
51494         return false;
51495     
51496         if(v !== undefined && v !== null){
51497             var r = this.findRecord(this.valueField || this.displayField, v);
51498             if(r){
51499                 this.select(this.store.indexOf(r), scrollIntoView);
51500                 return true;
51501             }
51502         }
51503         return false;
51504     },
51505
51506     /**
51507      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51508      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51509      * @param {Number} index The zero-based index of the list item to select
51510      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51511      * selected item if it is not currently in view (defaults to true)
51512      */
51513     select : function(index, scrollIntoView){
51514         Roo.log('select ');
51515         return  ;
51516         
51517         this.selectedIndex = index;
51518         this.view.select(index);
51519         if(scrollIntoView !== false){
51520             var el = this.view.getNode(index);
51521             if(el){
51522                 this.innerList.scrollChildIntoView(el, false);
51523             }
51524         }
51525     },
51526
51527       
51528
51529     // private
51530     validateBlur : function(){
51531         
51532         return;
51533         
51534     },
51535
51536     // private
51537     initQuery : function(){
51538         this.doQuery(this.getRawValue());
51539     },
51540
51541     // private
51542     doForce : function(){
51543         if(this.el.dom.value.length > 0){
51544             this.el.dom.value =
51545                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51546              
51547         }
51548     },
51549
51550     /**
51551      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51552      * query allowing the query action to be canceled if needed.
51553      * @param {String} query The SQL query to execute
51554      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51555      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51556      * saved in the current store (defaults to false)
51557      */
51558     doQuery : function(q, forceAll){
51559         
51560         Roo.log('doQuery?');
51561         if(q === undefined || q === null){
51562             q = '';
51563         }
51564         var qe = {
51565             query: q,
51566             forceAll: forceAll,
51567             combo: this,
51568             cancel:false
51569         };
51570         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51571             return false;
51572         }
51573         q = qe.query;
51574         forceAll = qe.forceAll;
51575         if(forceAll === true || (q.length >= this.minChars)){
51576             if(this.lastQuery != q || this.alwaysQuery){
51577                 this.lastQuery = q;
51578                 if(this.mode == 'local'){
51579                     this.selectedIndex = -1;
51580                     if(forceAll){
51581                         this.store.clearFilter();
51582                     }else{
51583                         this.store.filter(this.displayField, q);
51584                     }
51585                     this.onLoad();
51586                 }else{
51587                     this.store.baseParams[this.queryParam] = q;
51588                     this.store.load({
51589                         params: this.getParams(q)
51590                     });
51591                     this.expand();
51592                 }
51593             }else{
51594                 this.selectedIndex = -1;
51595                 this.onLoad();   
51596             }
51597         }
51598     },
51599
51600     // private
51601     getParams : function(q){
51602         var p = {};
51603         //p[this.queryParam] = q;
51604         if(this.pageSize){
51605             p.start = 0;
51606             p.limit = this.pageSize;
51607         }
51608         return p;
51609     },
51610
51611     /**
51612      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51613      */
51614     collapse : function(){
51615         
51616     },
51617
51618     // private
51619     collapseIf : function(e){
51620         
51621     },
51622
51623     /**
51624      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51625      */
51626     expand : function(){
51627         
51628     } ,
51629
51630     // private
51631      
51632
51633     /** 
51634     * @cfg {Boolean} grow 
51635     * @hide 
51636     */
51637     /** 
51638     * @cfg {Number} growMin 
51639     * @hide 
51640     */
51641     /** 
51642     * @cfg {Number} growMax 
51643     * @hide 
51644     */
51645     /**
51646      * @hide
51647      * @method autoSize
51648      */
51649     
51650     setWidth : function()
51651     {
51652         
51653     },
51654     getResizeEl : function(){
51655         return this.el;
51656     }
51657 });//<script type="text/javasscript">
51658  
51659
51660 /**
51661  * @class Roo.DDView
51662  * A DnD enabled version of Roo.View.
51663  * @param {Element/String} container The Element in which to create the View.
51664  * @param {String} tpl The template string used to create the markup for each element of the View
51665  * @param {Object} config The configuration properties. These include all the config options of
51666  * {@link Roo.View} plus some specific to this class.<br>
51667  * <p>
51668  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51669  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51670  * <p>
51671  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51672 .x-view-drag-insert-above {
51673         border-top:1px dotted #3366cc;
51674 }
51675 .x-view-drag-insert-below {
51676         border-bottom:1px dotted #3366cc;
51677 }
51678 </code></pre>
51679  * 
51680  */
51681  
51682 Roo.DDView = function(container, tpl, config) {
51683     Roo.DDView.superclass.constructor.apply(this, arguments);
51684     this.getEl().setStyle("outline", "0px none");
51685     this.getEl().unselectable();
51686     if (this.dragGroup) {
51687         this.setDraggable(this.dragGroup.split(","));
51688     }
51689     if (this.dropGroup) {
51690         this.setDroppable(this.dropGroup.split(","));
51691     }
51692     if (this.deletable) {
51693         this.setDeletable();
51694     }
51695     this.isDirtyFlag = false;
51696         this.addEvents({
51697                 "drop" : true
51698         });
51699 };
51700
51701 Roo.extend(Roo.DDView, Roo.View, {
51702 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51703 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51704 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51705 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51706
51707         isFormField: true,
51708
51709         reset: Roo.emptyFn,
51710         
51711         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51712
51713         validate: function() {
51714                 return true;
51715         },
51716         
51717         destroy: function() {
51718                 this.purgeListeners();
51719                 this.getEl.removeAllListeners();
51720                 this.getEl().remove();
51721                 if (this.dragZone) {
51722                         if (this.dragZone.destroy) {
51723                                 this.dragZone.destroy();
51724                         }
51725                 }
51726                 if (this.dropZone) {
51727                         if (this.dropZone.destroy) {
51728                                 this.dropZone.destroy();
51729                         }
51730                 }
51731         },
51732
51733 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51734         getName: function() {
51735                 return this.name;
51736         },
51737
51738 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51739         setValue: function(v) {
51740                 if (!this.store) {
51741                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51742                 }
51743                 var data = {};
51744                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51745                 this.store.proxy = new Roo.data.MemoryProxy(data);
51746                 this.store.load();
51747         },
51748
51749 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51750         getValue: function() {
51751                 var result = '(';
51752                 this.store.each(function(rec) {
51753                         result += rec.id + ',';
51754                 });
51755                 return result.substr(0, result.length - 1) + ')';
51756         },
51757         
51758         getIds: function() {
51759                 var i = 0, result = new Array(this.store.getCount());
51760                 this.store.each(function(rec) {
51761                         result[i++] = rec.id;
51762                 });
51763                 return result;
51764         },
51765         
51766         isDirty: function() {
51767                 return this.isDirtyFlag;
51768         },
51769
51770 /**
51771  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51772  *      whole Element becomes the target, and this causes the drop gesture to append.
51773  */
51774     getTargetFromEvent : function(e) {
51775                 var target = e.getTarget();
51776                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51777                 target = target.parentNode;
51778                 }
51779                 if (!target) {
51780                         target = this.el.dom.lastChild || this.el.dom;
51781                 }
51782                 return target;
51783     },
51784
51785 /**
51786  *      Create the drag data which consists of an object which has the property "ddel" as
51787  *      the drag proxy element. 
51788  */
51789     getDragData : function(e) {
51790         var target = this.findItemFromChild(e.getTarget());
51791                 if(target) {
51792                         this.handleSelection(e);
51793                         var selNodes = this.getSelectedNodes();
51794             var dragData = {
51795                 source: this,
51796                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51797                 nodes: selNodes,
51798                 records: []
51799                         };
51800                         var selectedIndices = this.getSelectedIndexes();
51801                         for (var i = 0; i < selectedIndices.length; i++) {
51802                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51803                         }
51804                         if (selNodes.length == 1) {
51805                                 dragData.ddel = target.cloneNode(true); // the div element
51806                         } else {
51807                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51808                                 div.className = 'multi-proxy';
51809                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51810                                         div.appendChild(selNodes[i].cloneNode(true));
51811                                 }
51812                                 dragData.ddel = div;
51813                         }
51814             //console.log(dragData)
51815             //console.log(dragData.ddel.innerHTML)
51816                         return dragData;
51817                 }
51818         //console.log('nodragData')
51819                 return false;
51820     },
51821     
51822 /**     Specify to which ddGroup items in this DDView may be dragged. */
51823     setDraggable: function(ddGroup) {
51824         if (ddGroup instanceof Array) {
51825                 Roo.each(ddGroup, this.setDraggable, this);
51826                 return;
51827         }
51828         if (this.dragZone) {
51829                 this.dragZone.addToGroup(ddGroup);
51830         } else {
51831                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51832                                 containerScroll: true,
51833                                 ddGroup: ddGroup 
51834
51835                         });
51836 //                      Draggability implies selection. DragZone's mousedown selects the element.
51837                         if (!this.multiSelect) { this.singleSelect = true; }
51838
51839 //                      Wire the DragZone's handlers up to methods in *this*
51840                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51841                 }
51842     },
51843
51844 /**     Specify from which ddGroup this DDView accepts drops. */
51845     setDroppable: function(ddGroup) {
51846         if (ddGroup instanceof Array) {
51847                 Roo.each(ddGroup, this.setDroppable, this);
51848                 return;
51849         }
51850         if (this.dropZone) {
51851                 this.dropZone.addToGroup(ddGroup);
51852         } else {
51853                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51854                                 containerScroll: true,
51855                                 ddGroup: ddGroup
51856                         });
51857
51858 //                      Wire the DropZone's handlers up to methods in *this*
51859                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51860                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51861                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51862                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51863                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51864                 }
51865     },
51866
51867 /**     Decide whether to drop above or below a View node. */
51868     getDropPoint : function(e, n, dd){
51869         if (n == this.el.dom) { return "above"; }
51870                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51871                 var c = t + (b - t) / 2;
51872                 var y = Roo.lib.Event.getPageY(e);
51873                 if(y <= c) {
51874                         return "above";
51875                 }else{
51876                         return "below";
51877                 }
51878     },
51879
51880     onNodeEnter : function(n, dd, e, data){
51881                 return false;
51882     },
51883     
51884     onNodeOver : function(n, dd, e, data){
51885                 var pt = this.getDropPoint(e, n, dd);
51886                 // set the insert point style on the target node
51887                 var dragElClass = this.dropNotAllowed;
51888                 if (pt) {
51889                         var targetElClass;
51890                         if (pt == "above"){
51891                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51892                                 targetElClass = "x-view-drag-insert-above";
51893                         } else {
51894                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51895                                 targetElClass = "x-view-drag-insert-below";
51896                         }
51897                         if (this.lastInsertClass != targetElClass){
51898                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51899                                 this.lastInsertClass = targetElClass;
51900                         }
51901                 }
51902                 return dragElClass;
51903         },
51904
51905     onNodeOut : function(n, dd, e, data){
51906                 this.removeDropIndicators(n);
51907     },
51908
51909     onNodeDrop : function(n, dd, e, data){
51910         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51911                 return false;
51912         }
51913         var pt = this.getDropPoint(e, n, dd);
51914                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51915                 if (pt == "below") { insertAt++; }
51916                 for (var i = 0; i < data.records.length; i++) {
51917                         var r = data.records[i];
51918                         var dup = this.store.getById(r.id);
51919                         if (dup && (dd != this.dragZone)) {
51920                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51921                         } else {
51922                                 if (data.copy) {
51923                                         this.store.insert(insertAt++, r.copy());
51924                                 } else {
51925                                         data.source.isDirtyFlag = true;
51926                                         r.store.remove(r);
51927                                         this.store.insert(insertAt++, r);
51928                                 }
51929                                 this.isDirtyFlag = true;
51930                         }
51931                 }
51932                 this.dragZone.cachedTarget = null;
51933                 return true;
51934     },
51935
51936     removeDropIndicators : function(n){
51937                 if(n){
51938                         Roo.fly(n).removeClass([
51939                                 "x-view-drag-insert-above",
51940                                 "x-view-drag-insert-below"]);
51941                         this.lastInsertClass = "_noclass";
51942                 }
51943     },
51944
51945 /**
51946  *      Utility method. Add a delete option to the DDView's context menu.
51947  *      @param {String} imageUrl The URL of the "delete" icon image.
51948  */
51949         setDeletable: function(imageUrl) {
51950                 if (!this.singleSelect && !this.multiSelect) {
51951                         this.singleSelect = true;
51952                 }
51953                 var c = this.getContextMenu();
51954                 this.contextMenu.on("itemclick", function(item) {
51955                         switch (item.id) {
51956                                 case "delete":
51957                                         this.remove(this.getSelectedIndexes());
51958                                         break;
51959                         }
51960                 }, this);
51961                 this.contextMenu.add({
51962                         icon: imageUrl,
51963                         id: "delete",
51964                         text: 'Delete'
51965                 });
51966         },
51967         
51968 /**     Return the context menu for this DDView. */
51969         getContextMenu: function() {
51970                 if (!this.contextMenu) {
51971 //                      Create the View's context menu
51972                         this.contextMenu = new Roo.menu.Menu({
51973                                 id: this.id + "-contextmenu"
51974                         });
51975                         this.el.on("contextmenu", this.showContextMenu, this);
51976                 }
51977                 return this.contextMenu;
51978         },
51979         
51980         disableContextMenu: function() {
51981                 if (this.contextMenu) {
51982                         this.el.un("contextmenu", this.showContextMenu, this);
51983                 }
51984         },
51985
51986         showContextMenu: function(e, item) {
51987         item = this.findItemFromChild(e.getTarget());
51988                 if (item) {
51989                         e.stopEvent();
51990                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51991                         this.contextMenu.showAt(e.getXY());
51992             }
51993     },
51994
51995 /**
51996  *      Remove {@link Roo.data.Record}s at the specified indices.
51997  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51998  */
51999     remove: function(selectedIndices) {
52000                 selectedIndices = [].concat(selectedIndices);
52001                 for (var i = 0; i < selectedIndices.length; i++) {
52002                         var rec = this.store.getAt(selectedIndices[i]);
52003                         this.store.remove(rec);
52004                 }
52005     },
52006
52007 /**
52008  *      Double click fires the event, but also, if this is draggable, and there is only one other
52009  *      related DropZone, it transfers the selected node.
52010  */
52011     onDblClick : function(e){
52012         var item = this.findItemFromChild(e.getTarget());
52013         if(item){
52014             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52015                 return false;
52016             }
52017             if (this.dragGroup) {
52018                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52019                     while (targets.indexOf(this.dropZone) > -1) {
52020                             targets.remove(this.dropZone);
52021                                 }
52022                     if (targets.length == 1) {
52023                                         this.dragZone.cachedTarget = null;
52024                         var el = Roo.get(targets[0].getEl());
52025                         var box = el.getBox(true);
52026                         targets[0].onNodeDrop(el.dom, {
52027                                 target: el.dom,
52028                                 xy: [box.x, box.y + box.height - 1]
52029                         }, null, this.getDragData(e));
52030                     }
52031                 }
52032         }
52033     },
52034     
52035     handleSelection: function(e) {
52036                 this.dragZone.cachedTarget = null;
52037         var item = this.findItemFromChild(e.getTarget());
52038         if (!item) {
52039                 this.clearSelections(true);
52040                 return;
52041         }
52042                 if (item && (this.multiSelect || this.singleSelect)){
52043                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52044                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52045                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52046                                 this.unselect(item);
52047                         } else {
52048                                 this.select(item, this.multiSelect && e.ctrlKey);
52049                                 this.lastSelection = item;
52050                         }
52051                 }
52052     },
52053
52054     onItemClick : function(item, index, e){
52055                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52056                         return false;
52057                 }
52058                 return true;
52059     },
52060
52061     unselect : function(nodeInfo, suppressEvent){
52062                 var node = this.getNode(nodeInfo);
52063                 if(node && this.isSelected(node)){
52064                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52065                                 Roo.fly(node).removeClass(this.selectedClass);
52066                                 this.selections.remove(node);
52067                                 if(!suppressEvent){
52068                                         this.fireEvent("selectionchange", this, this.selections);
52069                                 }
52070                         }
52071                 }
52072     }
52073 });
52074 /*
52075  * Based on:
52076  * Ext JS Library 1.1.1
52077  * Copyright(c) 2006-2007, Ext JS, LLC.
52078  *
52079  * Originally Released Under LGPL - original licence link has changed is not relivant.
52080  *
52081  * Fork - LGPL
52082  * <script type="text/javascript">
52083  */
52084  
52085 /**
52086  * @class Roo.LayoutManager
52087  * @extends Roo.util.Observable
52088  * Base class for layout managers.
52089  */
52090 Roo.LayoutManager = function(container, config){
52091     Roo.LayoutManager.superclass.constructor.call(this);
52092     this.el = Roo.get(container);
52093     // ie scrollbar fix
52094     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52095         document.body.scroll = "no";
52096     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52097         this.el.position('relative');
52098     }
52099     this.id = this.el.id;
52100     this.el.addClass("x-layout-container");
52101     /** false to disable window resize monitoring @type Boolean */
52102     this.monitorWindowResize = true;
52103     this.regions = {};
52104     this.addEvents({
52105         /**
52106          * @event layout
52107          * Fires when a layout is performed. 
52108          * @param {Roo.LayoutManager} this
52109          */
52110         "layout" : true,
52111         /**
52112          * @event regionresized
52113          * Fires when the user resizes a region. 
52114          * @param {Roo.LayoutRegion} region The resized region
52115          * @param {Number} newSize The new size (width for east/west, height for north/south)
52116          */
52117         "regionresized" : true,
52118         /**
52119          * @event regioncollapsed
52120          * Fires when a region is collapsed. 
52121          * @param {Roo.LayoutRegion} region The collapsed region
52122          */
52123         "regioncollapsed" : true,
52124         /**
52125          * @event regionexpanded
52126          * Fires when a region is expanded.  
52127          * @param {Roo.LayoutRegion} region The expanded region
52128          */
52129         "regionexpanded" : true
52130     });
52131     this.updating = false;
52132     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52133 };
52134
52135 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52136     /**
52137      * Returns true if this layout is currently being updated
52138      * @return {Boolean}
52139      */
52140     isUpdating : function(){
52141         return this.updating; 
52142     },
52143     
52144     /**
52145      * Suspend the LayoutManager from doing auto-layouts while
52146      * making multiple add or remove calls
52147      */
52148     beginUpdate : function(){
52149         this.updating = true;    
52150     },
52151     
52152     /**
52153      * Restore auto-layouts and optionally disable the manager from performing a layout
52154      * @param {Boolean} noLayout true to disable a layout update 
52155      */
52156     endUpdate : function(noLayout){
52157         this.updating = false;
52158         if(!noLayout){
52159             this.layout();
52160         }    
52161     },
52162     
52163     layout: function(){
52164         
52165     },
52166     
52167     onRegionResized : function(region, newSize){
52168         this.fireEvent("regionresized", region, newSize);
52169         this.layout();
52170     },
52171     
52172     onRegionCollapsed : function(region){
52173         this.fireEvent("regioncollapsed", region);
52174     },
52175     
52176     onRegionExpanded : function(region){
52177         this.fireEvent("regionexpanded", region);
52178     },
52179         
52180     /**
52181      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52182      * performs box-model adjustments.
52183      * @return {Object} The size as an object {width: (the width), height: (the height)}
52184      */
52185     getViewSize : function(){
52186         var size;
52187         if(this.el.dom != document.body){
52188             size = this.el.getSize();
52189         }else{
52190             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52191         }
52192         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52193         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52194         return size;
52195     },
52196     
52197     /**
52198      * Returns the Element this layout is bound to.
52199      * @return {Roo.Element}
52200      */
52201     getEl : function(){
52202         return this.el;
52203     },
52204     
52205     /**
52206      * Returns the specified region.
52207      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52208      * @return {Roo.LayoutRegion}
52209      */
52210     getRegion : function(target){
52211         return this.regions[target.toLowerCase()];
52212     },
52213     
52214     onWindowResize : function(){
52215         if(this.monitorWindowResize){
52216             this.layout();
52217         }
52218     }
52219 });/*
52220  * Based on:
52221  * Ext JS Library 1.1.1
52222  * Copyright(c) 2006-2007, Ext JS, LLC.
52223  *
52224  * Originally Released Under LGPL - original licence link has changed is not relivant.
52225  *
52226  * Fork - LGPL
52227  * <script type="text/javascript">
52228  */
52229 /**
52230  * @class Roo.BorderLayout
52231  * @extends Roo.LayoutManager
52232  * @children Roo.ContentPanel
52233  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52234  * please see: <br><br>
52235  * <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>
52236  * <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>
52237  * Example:
52238  <pre><code>
52239  var layout = new Roo.BorderLayout(document.body, {
52240     north: {
52241         initialSize: 25,
52242         titlebar: false
52243     },
52244     west: {
52245         split:true,
52246         initialSize: 200,
52247         minSize: 175,
52248         maxSize: 400,
52249         titlebar: true,
52250         collapsible: true
52251     },
52252     east: {
52253         split:true,
52254         initialSize: 202,
52255         minSize: 175,
52256         maxSize: 400,
52257         titlebar: true,
52258         collapsible: true
52259     },
52260     south: {
52261         split:true,
52262         initialSize: 100,
52263         minSize: 100,
52264         maxSize: 200,
52265         titlebar: true,
52266         collapsible: true
52267     },
52268     center: {
52269         titlebar: true,
52270         autoScroll:true,
52271         resizeTabs: true,
52272         minTabWidth: 50,
52273         preferredTabWidth: 150
52274     }
52275 });
52276
52277 // shorthand
52278 var CP = Roo.ContentPanel;
52279
52280 layout.beginUpdate();
52281 layout.add("north", new CP("north", "North"));
52282 layout.add("south", new CP("south", {title: "South", closable: true}));
52283 layout.add("west", new CP("west", {title: "West"}));
52284 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52285 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52286 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52287 layout.getRegion("center").showPanel("center1");
52288 layout.endUpdate();
52289 </code></pre>
52290
52291 <b>The container the layout is rendered into can be either the body element or any other element.
52292 If it is not the body element, the container needs to either be an absolute positioned element,
52293 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52294 the container size if it is not the body element.</b>
52295
52296 * @constructor
52297 * Create a new BorderLayout
52298 * @param {String/HTMLElement/Element} container The container this layout is bound to
52299 * @param {Object} config Configuration options
52300  */
52301 Roo.BorderLayout = function(container, config){
52302     config = config || {};
52303     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52304     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52305     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52306         var target = this.factory.validRegions[i];
52307         if(config[target]){
52308             this.addRegion(target, config[target]);
52309         }
52310     }
52311 };
52312
52313 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52314         
52315         /**
52316          * @cfg {Roo.LayoutRegion} east
52317          */
52318         /**
52319          * @cfg {Roo.LayoutRegion} west
52320          */
52321         /**
52322          * @cfg {Roo.LayoutRegion} north
52323          */
52324         /**
52325          * @cfg {Roo.LayoutRegion} south
52326          */
52327         /**
52328          * @cfg {Roo.LayoutRegion} center
52329          */
52330     /**
52331      * Creates and adds a new region if it doesn't already exist.
52332      * @param {String} target The target region key (north, south, east, west or center).
52333      * @param {Object} config The regions config object
52334      * @return {BorderLayoutRegion} The new region
52335      */
52336     addRegion : function(target, config){
52337         if(!this.regions[target]){
52338             var r = this.factory.create(target, this, config);
52339             this.bindRegion(target, r);
52340         }
52341         return this.regions[target];
52342     },
52343
52344     // private (kinda)
52345     bindRegion : function(name, r){
52346         this.regions[name] = r;
52347         r.on("visibilitychange", this.layout, this);
52348         r.on("paneladded", this.layout, this);
52349         r.on("panelremoved", this.layout, this);
52350         r.on("invalidated", this.layout, this);
52351         r.on("resized", this.onRegionResized, this);
52352         r.on("collapsed", this.onRegionCollapsed, this);
52353         r.on("expanded", this.onRegionExpanded, this);
52354     },
52355
52356     /**
52357      * Performs a layout update.
52358      */
52359     layout : function(){
52360         if(this.updating) {
52361             return;
52362         }
52363         var size = this.getViewSize();
52364         var w = size.width;
52365         var h = size.height;
52366         var centerW = w;
52367         var centerH = h;
52368         var centerY = 0;
52369         var centerX = 0;
52370         //var x = 0, y = 0;
52371
52372         var rs = this.regions;
52373         var north = rs["north"];
52374         var south = rs["south"]; 
52375         var west = rs["west"];
52376         var east = rs["east"];
52377         var center = rs["center"];
52378         //if(this.hideOnLayout){ // not supported anymore
52379             //c.el.setStyle("display", "none");
52380         //}
52381         if(north && north.isVisible()){
52382             var b = north.getBox();
52383             var m = north.getMargins();
52384             b.width = w - (m.left+m.right);
52385             b.x = m.left;
52386             b.y = m.top;
52387             centerY = b.height + b.y + m.bottom;
52388             centerH -= centerY;
52389             north.updateBox(this.safeBox(b));
52390         }
52391         if(south && south.isVisible()){
52392             var b = south.getBox();
52393             var m = south.getMargins();
52394             b.width = w - (m.left+m.right);
52395             b.x = m.left;
52396             var totalHeight = (b.height + m.top + m.bottom);
52397             b.y = h - totalHeight + m.top;
52398             centerH -= totalHeight;
52399             south.updateBox(this.safeBox(b));
52400         }
52401         if(west && west.isVisible()){
52402             var b = west.getBox();
52403             var m = west.getMargins();
52404             b.height = centerH - (m.top+m.bottom);
52405             b.x = m.left;
52406             b.y = centerY + m.top;
52407             var totalWidth = (b.width + m.left + m.right);
52408             centerX += totalWidth;
52409             centerW -= totalWidth;
52410             west.updateBox(this.safeBox(b));
52411         }
52412         if(east && east.isVisible()){
52413             var b = east.getBox();
52414             var m = east.getMargins();
52415             b.height = centerH - (m.top+m.bottom);
52416             var totalWidth = (b.width + m.left + m.right);
52417             b.x = w - totalWidth + m.left;
52418             b.y = centerY + m.top;
52419             centerW -= totalWidth;
52420             east.updateBox(this.safeBox(b));
52421         }
52422         if(center){
52423             var m = center.getMargins();
52424             var centerBox = {
52425                 x: centerX + m.left,
52426                 y: centerY + m.top,
52427                 width: centerW - (m.left+m.right),
52428                 height: centerH - (m.top+m.bottom)
52429             };
52430             //if(this.hideOnLayout){
52431                 //center.el.setStyle("display", "block");
52432             //}
52433             center.updateBox(this.safeBox(centerBox));
52434         }
52435         this.el.repaint();
52436         this.fireEvent("layout", this);
52437     },
52438
52439     // private
52440     safeBox : function(box){
52441         box.width = Math.max(0, box.width);
52442         box.height = Math.max(0, box.height);
52443         return box;
52444     },
52445
52446     /**
52447      * Adds a ContentPanel (or subclass) to this layout.
52448      * @param {String} target The target region key (north, south, east, west or center).
52449      * @param {Roo.ContentPanel} panel The panel to add
52450      * @return {Roo.ContentPanel} The added panel
52451      */
52452     add : function(target, panel){
52453          
52454         target = target.toLowerCase();
52455         return this.regions[target].add(panel);
52456     },
52457
52458     /**
52459      * Remove a ContentPanel (or subclass) to this layout.
52460      * @param {String} target The target region key (north, south, east, west or center).
52461      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52462      * @return {Roo.ContentPanel} The removed panel
52463      */
52464     remove : function(target, panel){
52465         target = target.toLowerCase();
52466         return this.regions[target].remove(panel);
52467     },
52468
52469     /**
52470      * Searches all regions for a panel with the specified id
52471      * @param {String} panelId
52472      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52473      */
52474     findPanel : function(panelId){
52475         var rs = this.regions;
52476         for(var target in rs){
52477             if(typeof rs[target] != "function"){
52478                 var p = rs[target].getPanel(panelId);
52479                 if(p){
52480                     return p;
52481                 }
52482             }
52483         }
52484         return null;
52485     },
52486
52487     /**
52488      * Searches all regions for a panel with the specified id and activates (shows) it.
52489      * @param {String/ContentPanel} panelId The panels id or the panel itself
52490      * @return {Roo.ContentPanel} The shown panel or null
52491      */
52492     showPanel : function(panelId) {
52493       var rs = this.regions;
52494       for(var target in rs){
52495          var r = rs[target];
52496          if(typeof r != "function"){
52497             if(r.hasPanel(panelId)){
52498                return r.showPanel(panelId);
52499             }
52500          }
52501       }
52502       return null;
52503    },
52504
52505    /**
52506      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52507      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52508      */
52509     restoreState : function(provider){
52510         if(!provider){
52511             provider = Roo.state.Manager;
52512         }
52513         var sm = new Roo.LayoutStateManager();
52514         sm.init(this, provider);
52515     },
52516
52517     /**
52518      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52519      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52520      * a valid ContentPanel config object.  Example:
52521      * <pre><code>
52522 // Create the main layout
52523 var layout = new Roo.BorderLayout('main-ct', {
52524     west: {
52525         split:true,
52526         minSize: 175,
52527         titlebar: true
52528     },
52529     center: {
52530         title:'Components'
52531     }
52532 }, 'main-ct');
52533
52534 // Create and add multiple ContentPanels at once via configs
52535 layout.batchAdd({
52536    west: {
52537        id: 'source-files',
52538        autoCreate:true,
52539        title:'Ext Source Files',
52540        autoScroll:true,
52541        fitToFrame:true
52542    },
52543    center : {
52544        el: cview,
52545        autoScroll:true,
52546        fitToFrame:true,
52547        toolbar: tb,
52548        resizeEl:'cbody'
52549    }
52550 });
52551 </code></pre>
52552      * @param {Object} regions An object containing ContentPanel configs by region name
52553      */
52554     batchAdd : function(regions){
52555         this.beginUpdate();
52556         for(var rname in regions){
52557             var lr = this.regions[rname];
52558             if(lr){
52559                 this.addTypedPanels(lr, regions[rname]);
52560             }
52561         }
52562         this.endUpdate();
52563     },
52564
52565     // private
52566     addTypedPanels : function(lr, ps){
52567         if(typeof ps == 'string'){
52568             lr.add(new Roo.ContentPanel(ps));
52569         }
52570         else if(ps instanceof Array){
52571             for(var i =0, len = ps.length; i < len; i++){
52572                 this.addTypedPanels(lr, ps[i]);
52573             }
52574         }
52575         else if(!ps.events){ // raw config?
52576             var el = ps.el;
52577             delete ps.el; // prevent conflict
52578             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52579         }
52580         else {  // panel object assumed!
52581             lr.add(ps);
52582         }
52583     },
52584     /**
52585      * Adds a xtype elements to the layout.
52586      * <pre><code>
52587
52588 layout.addxtype({
52589        xtype : 'ContentPanel',
52590        region: 'west',
52591        items: [ .... ]
52592    }
52593 );
52594
52595 layout.addxtype({
52596         xtype : 'NestedLayoutPanel',
52597         region: 'west',
52598         layout: {
52599            center: { },
52600            west: { }   
52601         },
52602         items : [ ... list of content panels or nested layout panels.. ]
52603    }
52604 );
52605 </code></pre>
52606      * @param {Object} cfg Xtype definition of item to add.
52607      */
52608     addxtype : function(cfg)
52609     {
52610         // basically accepts a pannel...
52611         // can accept a layout region..!?!?
52612         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52613         
52614         if (!cfg.xtype.match(/Panel$/)) {
52615             return false;
52616         }
52617         var ret = false;
52618         
52619         if (typeof(cfg.region) == 'undefined') {
52620             Roo.log("Failed to add Panel, region was not set");
52621             Roo.log(cfg);
52622             return false;
52623         }
52624         var region = cfg.region;
52625         delete cfg.region;
52626         
52627           
52628         var xitems = [];
52629         if (cfg.items) {
52630             xitems = cfg.items;
52631             delete cfg.items;
52632         }
52633         var nb = false;
52634         
52635         switch(cfg.xtype) 
52636         {
52637             case 'ContentPanel':  // ContentPanel (el, cfg)
52638             case 'ScrollPanel':  // ContentPanel (el, cfg)
52639             case 'ViewPanel': 
52640                 if(cfg.autoCreate) {
52641                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52642                 } else {
52643                     var el = this.el.createChild();
52644                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52645                 }
52646                 
52647                 this.add(region, ret);
52648                 break;
52649             
52650             
52651             case 'TreePanel': // our new panel!
52652                 cfg.el = this.el.createChild();
52653                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52654                 this.add(region, ret);
52655                 break;
52656             
52657             case 'NestedLayoutPanel': 
52658                 // create a new Layout (which is  a Border Layout...
52659                 var el = this.el.createChild();
52660                 var clayout = cfg.layout;
52661                 delete cfg.layout;
52662                 clayout.items   = clayout.items  || [];
52663                 // replace this exitems with the clayout ones..
52664                 xitems = clayout.items;
52665                  
52666                 
52667                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52668                     cfg.background = false;
52669                 }
52670                 var layout = new Roo.BorderLayout(el, clayout);
52671                 
52672                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52673                 //console.log('adding nested layout panel '  + cfg.toSource());
52674                 this.add(region, ret);
52675                 nb = {}; /// find first...
52676                 break;
52677                 
52678             case 'GridPanel': 
52679             
52680                 // needs grid and region
52681                 
52682                 //var el = this.getRegion(region).el.createChild();
52683                 var el = this.el.createChild();
52684                 // create the grid first...
52685                 
52686                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52687                 delete cfg.grid;
52688                 if (region == 'center' && this.active ) {
52689                     cfg.background = false;
52690                 }
52691                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52692                 
52693                 this.add(region, ret);
52694                 if (cfg.background) {
52695                     ret.on('activate', function(gp) {
52696                         if (!gp.grid.rendered) {
52697                             gp.grid.render();
52698                         }
52699                     });
52700                 } else {
52701                     grid.render();
52702                 }
52703                 break;
52704            
52705            
52706            
52707                 
52708                 
52709                 
52710             default:
52711                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52712                     
52713                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52714                     this.add(region, ret);
52715                 } else {
52716                 
52717                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52718                     return null;
52719                 }
52720                 
52721              // GridPanel (grid, cfg)
52722             
52723         }
52724         this.beginUpdate();
52725         // add children..
52726         var region = '';
52727         var abn = {};
52728         Roo.each(xitems, function(i)  {
52729             region = nb && i.region ? i.region : false;
52730             
52731             var add = ret.addxtype(i);
52732            
52733             if (region) {
52734                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52735                 if (!i.background) {
52736                     abn[region] = nb[region] ;
52737                 }
52738             }
52739             
52740         });
52741         this.endUpdate();
52742
52743         // make the last non-background panel active..
52744         //if (nb) { Roo.log(abn); }
52745         if (nb) {
52746             
52747             for(var r in abn) {
52748                 region = this.getRegion(r);
52749                 if (region) {
52750                     // tried using nb[r], but it does not work..
52751                      
52752                     region.showPanel(abn[r]);
52753                    
52754                 }
52755             }
52756         }
52757         return ret;
52758         
52759     }
52760 });
52761
52762 /**
52763  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52764  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52765  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52766  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52767  * <pre><code>
52768 // shorthand
52769 var CP = Roo.ContentPanel;
52770
52771 var layout = Roo.BorderLayout.create({
52772     north: {
52773         initialSize: 25,
52774         titlebar: false,
52775         panels: [new CP("north", "North")]
52776     },
52777     west: {
52778         split:true,
52779         initialSize: 200,
52780         minSize: 175,
52781         maxSize: 400,
52782         titlebar: true,
52783         collapsible: true,
52784         panels: [new CP("west", {title: "West"})]
52785     },
52786     east: {
52787         split:true,
52788         initialSize: 202,
52789         minSize: 175,
52790         maxSize: 400,
52791         titlebar: true,
52792         collapsible: true,
52793         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52794     },
52795     south: {
52796         split:true,
52797         initialSize: 100,
52798         minSize: 100,
52799         maxSize: 200,
52800         titlebar: true,
52801         collapsible: true,
52802         panels: [new CP("south", {title: "South", closable: true})]
52803     },
52804     center: {
52805         titlebar: true,
52806         autoScroll:true,
52807         resizeTabs: true,
52808         minTabWidth: 50,
52809         preferredTabWidth: 150,
52810         panels: [
52811             new CP("center1", {title: "Close Me", closable: true}),
52812             new CP("center2", {title: "Center Panel", closable: false})
52813         ]
52814     }
52815 }, document.body);
52816
52817 layout.getRegion("center").showPanel("center1");
52818 </code></pre>
52819  * @param config
52820  * @param targetEl
52821  */
52822 Roo.BorderLayout.create = function(config, targetEl){
52823     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52824     layout.beginUpdate();
52825     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52826     for(var j = 0, jlen = regions.length; j < jlen; j++){
52827         var lr = regions[j];
52828         if(layout.regions[lr] && config[lr].panels){
52829             var r = layout.regions[lr];
52830             var ps = config[lr].panels;
52831             layout.addTypedPanels(r, ps);
52832         }
52833     }
52834     layout.endUpdate();
52835     return layout;
52836 };
52837
52838 // private
52839 Roo.BorderLayout.RegionFactory = {
52840     // private
52841     validRegions : ["north","south","east","west","center"],
52842
52843     // private
52844     create : function(target, mgr, config){
52845         target = target.toLowerCase();
52846         if(config.lightweight || config.basic){
52847             return new Roo.BasicLayoutRegion(mgr, config, target);
52848         }
52849         switch(target){
52850             case "north":
52851                 return new Roo.NorthLayoutRegion(mgr, config);
52852             case "south":
52853                 return new Roo.SouthLayoutRegion(mgr, config);
52854             case "east":
52855                 return new Roo.EastLayoutRegion(mgr, config);
52856             case "west":
52857                 return new Roo.WestLayoutRegion(mgr, config);
52858             case "center":
52859                 return new Roo.CenterLayoutRegion(mgr, config);
52860         }
52861         throw 'Layout region "'+target+'" not supported.';
52862     }
52863 };/*
52864  * Based on:
52865  * Ext JS Library 1.1.1
52866  * Copyright(c) 2006-2007, Ext JS, LLC.
52867  *
52868  * Originally Released Under LGPL - original licence link has changed is not relivant.
52869  *
52870  * Fork - LGPL
52871  * <script type="text/javascript">
52872  */
52873  
52874 /**
52875  * @class Roo.BasicLayoutRegion
52876  * @extends Roo.util.Observable
52877  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52878  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52879  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52880  */
52881 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52882     this.mgr = mgr;
52883     this.position  = pos;
52884     this.events = {
52885         /**
52886          * @scope Roo.BasicLayoutRegion
52887          */
52888         
52889         /**
52890          * @event beforeremove
52891          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52892          * @param {Roo.LayoutRegion} this
52893          * @param {Roo.ContentPanel} panel The panel
52894          * @param {Object} e The cancel event object
52895          */
52896         "beforeremove" : true,
52897         /**
52898          * @event invalidated
52899          * Fires when the layout for this region is changed.
52900          * @param {Roo.LayoutRegion} this
52901          */
52902         "invalidated" : true,
52903         /**
52904          * @event visibilitychange
52905          * Fires when this region is shown or hidden 
52906          * @param {Roo.LayoutRegion} this
52907          * @param {Boolean} visibility true or false
52908          */
52909         "visibilitychange" : true,
52910         /**
52911          * @event paneladded
52912          * Fires when a panel is added. 
52913          * @param {Roo.LayoutRegion} this
52914          * @param {Roo.ContentPanel} panel The panel
52915          */
52916         "paneladded" : true,
52917         /**
52918          * @event panelremoved
52919          * Fires when a panel is removed. 
52920          * @param {Roo.LayoutRegion} this
52921          * @param {Roo.ContentPanel} panel The panel
52922          */
52923         "panelremoved" : true,
52924         /**
52925          * @event beforecollapse
52926          * Fires when this region before collapse.
52927          * @param {Roo.LayoutRegion} this
52928          */
52929         "beforecollapse" : true,
52930         /**
52931          * @event collapsed
52932          * Fires when this region is collapsed.
52933          * @param {Roo.LayoutRegion} this
52934          */
52935         "collapsed" : true,
52936         /**
52937          * @event expanded
52938          * Fires when this region is expanded.
52939          * @param {Roo.LayoutRegion} this
52940          */
52941         "expanded" : true,
52942         /**
52943          * @event slideshow
52944          * Fires when this region is slid into view.
52945          * @param {Roo.LayoutRegion} this
52946          */
52947         "slideshow" : true,
52948         /**
52949          * @event slidehide
52950          * Fires when this region slides out of view. 
52951          * @param {Roo.LayoutRegion} this
52952          */
52953         "slidehide" : true,
52954         /**
52955          * @event panelactivated
52956          * Fires when a panel is activated. 
52957          * @param {Roo.LayoutRegion} this
52958          * @param {Roo.ContentPanel} panel The activated panel
52959          */
52960         "panelactivated" : true,
52961         /**
52962          * @event resized
52963          * Fires when the user resizes this region. 
52964          * @param {Roo.LayoutRegion} this
52965          * @param {Number} newSize The new size (width for east/west, height for north/south)
52966          */
52967         "resized" : true
52968     };
52969     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52970     this.panels = new Roo.util.MixedCollection();
52971     this.panels.getKey = this.getPanelId.createDelegate(this);
52972     this.box = null;
52973     this.activePanel = null;
52974     // ensure listeners are added...
52975     
52976     if (config.listeners || config.events) {
52977         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52978             listeners : config.listeners || {},
52979             events : config.events || {}
52980         });
52981     }
52982     
52983     if(skipConfig !== true){
52984         this.applyConfig(config);
52985     }
52986 };
52987
52988 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52989     getPanelId : function(p){
52990         return p.getId();
52991     },
52992     
52993     applyConfig : function(config){
52994         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52995         this.config = config;
52996         
52997     },
52998     
52999     /**
53000      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53001      * the width, for horizontal (north, south) the height.
53002      * @param {Number} newSize The new width or height
53003      */
53004     resizeTo : function(newSize){
53005         var el = this.el ? this.el :
53006                  (this.activePanel ? this.activePanel.getEl() : null);
53007         if(el){
53008             switch(this.position){
53009                 case "east":
53010                 case "west":
53011                     el.setWidth(newSize);
53012                     this.fireEvent("resized", this, newSize);
53013                 break;
53014                 case "north":
53015                 case "south":
53016                     el.setHeight(newSize);
53017                     this.fireEvent("resized", this, newSize);
53018                 break;                
53019             }
53020         }
53021     },
53022     
53023     getBox : function(){
53024         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53025     },
53026     
53027     getMargins : function(){
53028         return this.margins;
53029     },
53030     
53031     updateBox : function(box){
53032         this.box = box;
53033         var el = this.activePanel.getEl();
53034         el.dom.style.left = box.x + "px";
53035         el.dom.style.top = box.y + "px";
53036         this.activePanel.setSize(box.width, box.height);
53037     },
53038     
53039     /**
53040      * Returns the container element for this region.
53041      * @return {Roo.Element}
53042      */
53043     getEl : function(){
53044         return this.activePanel;
53045     },
53046     
53047     /**
53048      * Returns true if this region is currently visible.
53049      * @return {Boolean}
53050      */
53051     isVisible : function(){
53052         return this.activePanel ? true : false;
53053     },
53054     
53055     setActivePanel : function(panel){
53056         panel = this.getPanel(panel);
53057         if(this.activePanel && this.activePanel != panel){
53058             this.activePanel.setActiveState(false);
53059             this.activePanel.getEl().setLeftTop(-10000,-10000);
53060         }
53061         this.activePanel = panel;
53062         panel.setActiveState(true);
53063         if(this.box){
53064             panel.setSize(this.box.width, this.box.height);
53065         }
53066         this.fireEvent("panelactivated", this, panel);
53067         this.fireEvent("invalidated");
53068     },
53069     
53070     /**
53071      * Show the specified panel.
53072      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53073      * @return {Roo.ContentPanel} The shown panel or null
53074      */
53075     showPanel : function(panel){
53076         if(panel = this.getPanel(panel)){
53077             this.setActivePanel(panel);
53078         }
53079         return panel;
53080     },
53081     
53082     /**
53083      * Get the active panel for this region.
53084      * @return {Roo.ContentPanel} The active panel or null
53085      */
53086     getActivePanel : function(){
53087         return this.activePanel;
53088     },
53089     
53090     /**
53091      * Add the passed ContentPanel(s)
53092      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53093      * @return {Roo.ContentPanel} The panel added (if only one was added)
53094      */
53095     add : function(panel){
53096         if(arguments.length > 1){
53097             for(var i = 0, len = arguments.length; i < len; i++) {
53098                 this.add(arguments[i]);
53099             }
53100             return null;
53101         }
53102         if(this.hasPanel(panel)){
53103             this.showPanel(panel);
53104             return panel;
53105         }
53106         var el = panel.getEl();
53107         if(el.dom.parentNode != this.mgr.el.dom){
53108             this.mgr.el.dom.appendChild(el.dom);
53109         }
53110         if(panel.setRegion){
53111             panel.setRegion(this);
53112         }
53113         this.panels.add(panel);
53114         el.setStyle("position", "absolute");
53115         if(!panel.background){
53116             this.setActivePanel(panel);
53117             if(this.config.initialSize && this.panels.getCount()==1){
53118                 this.resizeTo(this.config.initialSize);
53119             }
53120         }
53121         this.fireEvent("paneladded", this, panel);
53122         return panel;
53123     },
53124     
53125     /**
53126      * Returns true if the panel is in this region.
53127      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53128      * @return {Boolean}
53129      */
53130     hasPanel : function(panel){
53131         if(typeof panel == "object"){ // must be panel obj
53132             panel = panel.getId();
53133         }
53134         return this.getPanel(panel) ? true : false;
53135     },
53136     
53137     /**
53138      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53139      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53140      * @param {Boolean} preservePanel Overrides the config preservePanel option
53141      * @return {Roo.ContentPanel} The panel that was removed
53142      */
53143     remove : function(panel, preservePanel){
53144         panel = this.getPanel(panel);
53145         if(!panel){
53146             return null;
53147         }
53148         var e = {};
53149         this.fireEvent("beforeremove", this, panel, e);
53150         if(e.cancel === true){
53151             return null;
53152         }
53153         var panelId = panel.getId();
53154         this.panels.removeKey(panelId);
53155         return panel;
53156     },
53157     
53158     /**
53159      * Returns the panel specified or null if it's not in this region.
53160      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53161      * @return {Roo.ContentPanel}
53162      */
53163     getPanel : function(id){
53164         if(typeof id == "object"){ // must be panel obj
53165             return id;
53166         }
53167         return this.panels.get(id);
53168     },
53169     
53170     /**
53171      * Returns this regions position (north/south/east/west/center).
53172      * @return {String} 
53173      */
53174     getPosition: function(){
53175         return this.position;    
53176     }
53177 });/*
53178  * Based on:
53179  * Ext JS Library 1.1.1
53180  * Copyright(c) 2006-2007, Ext JS, LLC.
53181  *
53182  * Originally Released Under LGPL - original licence link has changed is not relivant.
53183  *
53184  * Fork - LGPL
53185  * <script type="text/javascript">
53186  */
53187  
53188 /**
53189  * @class Roo.LayoutRegion
53190  * @extends Roo.BasicLayoutRegion
53191  * This class represents a region in a layout manager.
53192  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53193  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53194  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53195  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53196  * @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})
53197  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53198  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53199  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53200  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53201  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53202  * @cfg {String}    title           The title for the region (overrides panel titles)
53203  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53204  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53205  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53206  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53207  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53208  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53209  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53210  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53211  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53212  * @cfg {Boolean}   showPin         True to show a pin button
53213  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53214  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53215  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53216  * @cfg {Number}    width           For East/West panels
53217  * @cfg {Number}    height          For North/South panels
53218  * @cfg {Boolean}   split           To show the splitter
53219  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53220  */
53221 Roo.LayoutRegion = function(mgr, config, pos){
53222     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53223     var dh = Roo.DomHelper;
53224     /** This region's container element 
53225     * @type Roo.Element */
53226     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53227     /** This region's title element 
53228     * @type Roo.Element */
53229
53230     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53231         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53232         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53233     ]}, true);
53234     this.titleEl.enableDisplayMode();
53235     /** This region's title text element 
53236     * @type HTMLElement */
53237     this.titleTextEl = this.titleEl.dom.firstChild;
53238     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53239     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53240     this.closeBtn.enableDisplayMode();
53241     this.closeBtn.on("click", this.closeClicked, this);
53242     this.closeBtn.hide();
53243
53244     this.createBody(config);
53245     this.visible = true;
53246     this.collapsed = false;
53247
53248     if(config.hideWhenEmpty){
53249         this.hide();
53250         this.on("paneladded", this.validateVisibility, this);
53251         this.on("panelremoved", this.validateVisibility, this);
53252     }
53253     this.applyConfig(config);
53254 };
53255
53256 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53257
53258     createBody : function(){
53259         /** This region's body element 
53260         * @type Roo.Element */
53261         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53262     },
53263
53264     applyConfig : function(c){
53265         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53266             var dh = Roo.DomHelper;
53267             if(c.titlebar !== false){
53268                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53269                 this.collapseBtn.on("click", this.collapse, this);
53270                 this.collapseBtn.enableDisplayMode();
53271
53272                 if(c.showPin === true || this.showPin){
53273                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53274                     this.stickBtn.enableDisplayMode();
53275                     this.stickBtn.on("click", this.expand, this);
53276                     this.stickBtn.hide();
53277                 }
53278             }
53279             /** This region's collapsed element
53280             * @type Roo.Element */
53281             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53282                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53283             ]}, true);
53284             if(c.floatable !== false){
53285                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53286                this.collapsedEl.on("click", this.collapseClick, this);
53287             }
53288
53289             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53290                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53291                    id: "message", unselectable: "on", style:{"float":"left"}});
53292                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53293              }
53294             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53295             this.expandBtn.on("click", this.expand, this);
53296         }
53297         if(this.collapseBtn){
53298             this.collapseBtn.setVisible(c.collapsible == true);
53299         }
53300         this.cmargins = c.cmargins || this.cmargins ||
53301                          (this.position == "west" || this.position == "east" ?
53302                              {top: 0, left: 2, right:2, bottom: 0} :
53303                              {top: 2, left: 0, right:0, bottom: 2});
53304         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53305         this.bottomTabs = c.tabPosition != "top";
53306         this.autoScroll = c.autoScroll || false;
53307         if(this.autoScroll){
53308             this.bodyEl.setStyle("overflow", "auto");
53309         }else{
53310             this.bodyEl.setStyle("overflow", "hidden");
53311         }
53312         //if(c.titlebar !== false){
53313             if((!c.titlebar && !c.title) || c.titlebar === false){
53314                 this.titleEl.hide();
53315             }else{
53316                 this.titleEl.show();
53317                 if(c.title){
53318                     this.titleTextEl.innerHTML = c.title;
53319                 }
53320             }
53321         //}
53322         this.duration = c.duration || .30;
53323         this.slideDuration = c.slideDuration || .45;
53324         this.config = c;
53325         if(c.collapsed){
53326             this.collapse(true);
53327         }
53328         if(c.hidden){
53329             this.hide();
53330         }
53331     },
53332     /**
53333      * Returns true if this region is currently visible.
53334      * @return {Boolean}
53335      */
53336     isVisible : function(){
53337         return this.visible;
53338     },
53339
53340     /**
53341      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53342      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53343      */
53344     setCollapsedTitle : function(title){
53345         title = title || "&#160;";
53346         if(this.collapsedTitleTextEl){
53347             this.collapsedTitleTextEl.innerHTML = title;
53348         }
53349     },
53350
53351     getBox : function(){
53352         var b;
53353         if(!this.collapsed){
53354             b = this.el.getBox(false, true);
53355         }else{
53356             b = this.collapsedEl.getBox(false, true);
53357         }
53358         return b;
53359     },
53360
53361     getMargins : function(){
53362         return this.collapsed ? this.cmargins : this.margins;
53363     },
53364
53365     highlight : function(){
53366         this.el.addClass("x-layout-panel-dragover");
53367     },
53368
53369     unhighlight : function(){
53370         this.el.removeClass("x-layout-panel-dragover");
53371     },
53372
53373     updateBox : function(box){
53374         this.box = box;
53375         if(!this.collapsed){
53376             this.el.dom.style.left = box.x + "px";
53377             this.el.dom.style.top = box.y + "px";
53378             this.updateBody(box.width, box.height);
53379         }else{
53380             this.collapsedEl.dom.style.left = box.x + "px";
53381             this.collapsedEl.dom.style.top = box.y + "px";
53382             this.collapsedEl.setSize(box.width, box.height);
53383         }
53384         if(this.tabs){
53385             this.tabs.autoSizeTabs();
53386         }
53387     },
53388
53389     updateBody : function(w, h){
53390         if(w !== null){
53391             this.el.setWidth(w);
53392             w -= this.el.getBorderWidth("rl");
53393             if(this.config.adjustments){
53394                 w += this.config.adjustments[0];
53395             }
53396         }
53397         if(h !== null){
53398             this.el.setHeight(h);
53399             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53400             h -= this.el.getBorderWidth("tb");
53401             if(this.config.adjustments){
53402                 h += this.config.adjustments[1];
53403             }
53404             this.bodyEl.setHeight(h);
53405             if(this.tabs){
53406                 h = this.tabs.syncHeight(h);
53407             }
53408         }
53409         if(this.panelSize){
53410             w = w !== null ? w : this.panelSize.width;
53411             h = h !== null ? h : this.panelSize.height;
53412         }
53413         if(this.activePanel){
53414             var el = this.activePanel.getEl();
53415             w = w !== null ? w : el.getWidth();
53416             h = h !== null ? h : el.getHeight();
53417             this.panelSize = {width: w, height: h};
53418             this.activePanel.setSize(w, h);
53419         }
53420         if(Roo.isIE && this.tabs){
53421             this.tabs.el.repaint();
53422         }
53423     },
53424
53425     /**
53426      * Returns the container element for this region.
53427      * @return {Roo.Element}
53428      */
53429     getEl : function(){
53430         return this.el;
53431     },
53432
53433     /**
53434      * Hides this region.
53435      */
53436     hide : function(){
53437         if(!this.collapsed){
53438             this.el.dom.style.left = "-2000px";
53439             this.el.hide();
53440         }else{
53441             this.collapsedEl.dom.style.left = "-2000px";
53442             this.collapsedEl.hide();
53443         }
53444         this.visible = false;
53445         this.fireEvent("visibilitychange", this, false);
53446     },
53447
53448     /**
53449      * Shows this region if it was previously hidden.
53450      */
53451     show : function(){
53452         if(!this.collapsed){
53453             this.el.show();
53454         }else{
53455             this.collapsedEl.show();
53456         }
53457         this.visible = true;
53458         this.fireEvent("visibilitychange", this, true);
53459     },
53460
53461     closeClicked : function(){
53462         if(this.activePanel){
53463             this.remove(this.activePanel);
53464         }
53465     },
53466
53467     collapseClick : function(e){
53468         if(this.isSlid){
53469            e.stopPropagation();
53470            this.slideIn();
53471         }else{
53472            e.stopPropagation();
53473            this.slideOut();
53474         }
53475     },
53476
53477     /**
53478      * Collapses this region.
53479      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53480      */
53481     collapse : function(skipAnim, skipCheck){
53482         if(this.collapsed) {
53483             return;
53484         }
53485         
53486         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53487             
53488             this.collapsed = true;
53489             if(this.split){
53490                 this.split.el.hide();
53491             }
53492             if(this.config.animate && skipAnim !== true){
53493                 this.fireEvent("invalidated", this);
53494                 this.animateCollapse();
53495             }else{
53496                 this.el.setLocation(-20000,-20000);
53497                 this.el.hide();
53498                 this.collapsedEl.show();
53499                 this.fireEvent("collapsed", this);
53500                 this.fireEvent("invalidated", this);
53501             }
53502         }
53503         
53504     },
53505
53506     animateCollapse : function(){
53507         // overridden
53508     },
53509
53510     /**
53511      * Expands this region if it was previously collapsed.
53512      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53513      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53514      */
53515     expand : function(e, skipAnim){
53516         if(e) {
53517             e.stopPropagation();
53518         }
53519         if(!this.collapsed || this.el.hasActiveFx()) {
53520             return;
53521         }
53522         if(this.isSlid){
53523             this.afterSlideIn();
53524             skipAnim = true;
53525         }
53526         this.collapsed = false;
53527         if(this.config.animate && skipAnim !== true){
53528             this.animateExpand();
53529         }else{
53530             this.el.show();
53531             if(this.split){
53532                 this.split.el.show();
53533             }
53534             this.collapsedEl.setLocation(-2000,-2000);
53535             this.collapsedEl.hide();
53536             this.fireEvent("invalidated", this);
53537             this.fireEvent("expanded", this);
53538         }
53539     },
53540
53541     animateExpand : function(){
53542         // overridden
53543     },
53544
53545     initTabs : function()
53546     {
53547         this.bodyEl.setStyle("overflow", "hidden");
53548         var ts = new Roo.TabPanel(
53549                 this.bodyEl.dom,
53550                 {
53551                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53552                     disableTooltips: this.config.disableTabTips,
53553                     toolbar : this.config.toolbar
53554                 }
53555         );
53556         if(this.config.hideTabs){
53557             ts.stripWrap.setDisplayed(false);
53558         }
53559         this.tabs = ts;
53560         ts.resizeTabs = this.config.resizeTabs === true;
53561         ts.minTabWidth = this.config.minTabWidth || 40;
53562         ts.maxTabWidth = this.config.maxTabWidth || 250;
53563         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53564         ts.monitorResize = false;
53565         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53566         ts.bodyEl.addClass('x-layout-tabs-body');
53567         this.panels.each(this.initPanelAsTab, this);
53568     },
53569
53570     initPanelAsTab : function(panel){
53571         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53572                     this.config.closeOnTab && panel.isClosable());
53573         if(panel.tabTip !== undefined){
53574             ti.setTooltip(panel.tabTip);
53575         }
53576         ti.on("activate", function(){
53577               this.setActivePanel(panel);
53578         }, this);
53579         if(this.config.closeOnTab){
53580             ti.on("beforeclose", function(t, e){
53581                 e.cancel = true;
53582                 this.remove(panel);
53583             }, this);
53584         }
53585         return ti;
53586     },
53587
53588     updatePanelTitle : function(panel, title){
53589         if(this.activePanel == panel){
53590             this.updateTitle(title);
53591         }
53592         if(this.tabs){
53593             var ti = this.tabs.getTab(panel.getEl().id);
53594             ti.setText(title);
53595             if(panel.tabTip !== undefined){
53596                 ti.setTooltip(panel.tabTip);
53597             }
53598         }
53599     },
53600
53601     updateTitle : function(title){
53602         if(this.titleTextEl && !this.config.title){
53603             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53604         }
53605     },
53606
53607     setActivePanel : function(panel){
53608         panel = this.getPanel(panel);
53609         if(this.activePanel && this.activePanel != panel){
53610             this.activePanel.setActiveState(false);
53611         }
53612         this.activePanel = panel;
53613         panel.setActiveState(true);
53614         if(this.panelSize){
53615             panel.setSize(this.panelSize.width, this.panelSize.height);
53616         }
53617         if(this.closeBtn){
53618             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53619         }
53620         this.updateTitle(panel.getTitle());
53621         if(this.tabs){
53622             this.fireEvent("invalidated", this);
53623         }
53624         this.fireEvent("panelactivated", this, panel);
53625     },
53626
53627     /**
53628      * Shows the specified panel.
53629      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53630      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53631      */
53632     showPanel : function(panel)
53633     {
53634         panel = this.getPanel(panel);
53635         if(panel){
53636             if(this.tabs){
53637                 var tab = this.tabs.getTab(panel.getEl().id);
53638                 if(tab.isHidden()){
53639                     this.tabs.unhideTab(tab.id);
53640                 }
53641                 tab.activate();
53642             }else{
53643                 this.setActivePanel(panel);
53644             }
53645         }
53646         return panel;
53647     },
53648
53649     /**
53650      * Get the active panel for this region.
53651      * @return {Roo.ContentPanel} The active panel or null
53652      */
53653     getActivePanel : function(){
53654         return this.activePanel;
53655     },
53656
53657     validateVisibility : function(){
53658         if(this.panels.getCount() < 1){
53659             this.updateTitle("&#160;");
53660             this.closeBtn.hide();
53661             this.hide();
53662         }else{
53663             if(!this.isVisible()){
53664                 this.show();
53665             }
53666         }
53667     },
53668
53669     /**
53670      * Adds the passed ContentPanel(s) to this region.
53671      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53672      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53673      */
53674     add : function(panel){
53675         if(arguments.length > 1){
53676             for(var i = 0, len = arguments.length; i < len; i++) {
53677                 this.add(arguments[i]);
53678             }
53679             return null;
53680         }
53681         if(this.hasPanel(panel)){
53682             this.showPanel(panel);
53683             return panel;
53684         }
53685         panel.setRegion(this);
53686         this.panels.add(panel);
53687         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53688             this.bodyEl.dom.appendChild(panel.getEl().dom);
53689             if(panel.background !== true){
53690                 this.setActivePanel(panel);
53691             }
53692             this.fireEvent("paneladded", this, panel);
53693             return panel;
53694         }
53695         if(!this.tabs){
53696             this.initTabs();
53697         }else{
53698             this.initPanelAsTab(panel);
53699         }
53700         if(panel.background !== true){
53701             this.tabs.activate(panel.getEl().id);
53702         }
53703         this.fireEvent("paneladded", this, panel);
53704         return panel;
53705     },
53706
53707     /**
53708      * Hides the tab for the specified panel.
53709      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53710      */
53711     hidePanel : function(panel){
53712         if(this.tabs && (panel = this.getPanel(panel))){
53713             this.tabs.hideTab(panel.getEl().id);
53714         }
53715     },
53716
53717     /**
53718      * Unhides the tab for a previously hidden panel.
53719      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53720      */
53721     unhidePanel : function(panel){
53722         if(this.tabs && (panel = this.getPanel(panel))){
53723             this.tabs.unhideTab(panel.getEl().id);
53724         }
53725     },
53726
53727     clearPanels : function(){
53728         while(this.panels.getCount() > 0){
53729              this.remove(this.panels.first());
53730         }
53731     },
53732
53733     /**
53734      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53735      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53736      * @param {Boolean} preservePanel Overrides the config preservePanel option
53737      * @return {Roo.ContentPanel} The panel that was removed
53738      */
53739     remove : function(panel, preservePanel){
53740         panel = this.getPanel(panel);
53741         if(!panel){
53742             return null;
53743         }
53744         var e = {};
53745         this.fireEvent("beforeremove", this, panel, e);
53746         if(e.cancel === true){
53747             return null;
53748         }
53749         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53750         var panelId = panel.getId();
53751         this.panels.removeKey(panelId);
53752         if(preservePanel){
53753             document.body.appendChild(panel.getEl().dom);
53754         }
53755         if(this.tabs){
53756             this.tabs.removeTab(panel.getEl().id);
53757         }else if (!preservePanel){
53758             this.bodyEl.dom.removeChild(panel.getEl().dom);
53759         }
53760         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53761             var p = this.panels.first();
53762             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53763             tempEl.appendChild(p.getEl().dom);
53764             this.bodyEl.update("");
53765             this.bodyEl.dom.appendChild(p.getEl().dom);
53766             tempEl = null;
53767             this.updateTitle(p.getTitle());
53768             this.tabs = null;
53769             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53770             this.setActivePanel(p);
53771         }
53772         panel.setRegion(null);
53773         if(this.activePanel == panel){
53774             this.activePanel = null;
53775         }
53776         if(this.config.autoDestroy !== false && preservePanel !== true){
53777             try{panel.destroy();}catch(e){}
53778         }
53779         this.fireEvent("panelremoved", this, panel);
53780         return panel;
53781     },
53782
53783     /**
53784      * Returns the TabPanel component used by this region
53785      * @return {Roo.TabPanel}
53786      */
53787     getTabs : function(){
53788         return this.tabs;
53789     },
53790
53791     createTool : function(parentEl, className){
53792         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53793             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53794         btn.addClassOnOver("x-layout-tools-button-over");
53795         return btn;
53796     }
53797 });/*
53798  * Based on:
53799  * Ext JS Library 1.1.1
53800  * Copyright(c) 2006-2007, Ext JS, LLC.
53801  *
53802  * Originally Released Under LGPL - original licence link has changed is not relivant.
53803  *
53804  * Fork - LGPL
53805  * <script type="text/javascript">
53806  */
53807  
53808
53809
53810 /**
53811  * @class Roo.SplitLayoutRegion
53812  * @extends Roo.LayoutRegion
53813  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53814  */
53815 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53816     this.cursor = cursor;
53817     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53818 };
53819
53820 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53821     splitTip : "Drag to resize.",
53822     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53823     useSplitTips : false,
53824
53825     applyConfig : function(config){
53826         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53827         if(config.split){
53828             if(!this.split){
53829                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53830                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53831                 /** The SplitBar for this region 
53832                 * @type Roo.SplitBar */
53833                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53834                 this.split.on("moved", this.onSplitMove, this);
53835                 this.split.useShim = config.useShim === true;
53836                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53837                 if(this.useSplitTips){
53838                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53839                 }
53840                 if(config.collapsible){
53841                     this.split.el.on("dblclick", this.collapse,  this);
53842                 }
53843             }
53844             if(typeof config.minSize != "undefined"){
53845                 this.split.minSize = config.minSize;
53846             }
53847             if(typeof config.maxSize != "undefined"){
53848                 this.split.maxSize = config.maxSize;
53849             }
53850             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53851                 this.hideSplitter();
53852             }
53853         }
53854     },
53855
53856     getHMaxSize : function(){
53857          var cmax = this.config.maxSize || 10000;
53858          var center = this.mgr.getRegion("center");
53859          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53860     },
53861
53862     getVMaxSize : function(){
53863          var cmax = this.config.maxSize || 10000;
53864          var center = this.mgr.getRegion("center");
53865          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53866     },
53867
53868     onSplitMove : function(split, newSize){
53869         this.fireEvent("resized", this, newSize);
53870     },
53871     
53872     /** 
53873      * Returns the {@link Roo.SplitBar} for this region.
53874      * @return {Roo.SplitBar}
53875      */
53876     getSplitBar : function(){
53877         return this.split;
53878     },
53879     
53880     hide : function(){
53881         this.hideSplitter();
53882         Roo.SplitLayoutRegion.superclass.hide.call(this);
53883     },
53884
53885     hideSplitter : function(){
53886         if(this.split){
53887             this.split.el.setLocation(-2000,-2000);
53888             this.split.el.hide();
53889         }
53890     },
53891
53892     show : function(){
53893         if(this.split){
53894             this.split.el.show();
53895         }
53896         Roo.SplitLayoutRegion.superclass.show.call(this);
53897     },
53898     
53899     beforeSlide: function(){
53900         if(Roo.isGecko){// firefox overflow auto bug workaround
53901             this.bodyEl.clip();
53902             if(this.tabs) {
53903                 this.tabs.bodyEl.clip();
53904             }
53905             if(this.activePanel){
53906                 this.activePanel.getEl().clip();
53907                 
53908                 if(this.activePanel.beforeSlide){
53909                     this.activePanel.beforeSlide();
53910                 }
53911             }
53912         }
53913     },
53914     
53915     afterSlide : function(){
53916         if(Roo.isGecko){// firefox overflow auto bug workaround
53917             this.bodyEl.unclip();
53918             if(this.tabs) {
53919                 this.tabs.bodyEl.unclip();
53920             }
53921             if(this.activePanel){
53922                 this.activePanel.getEl().unclip();
53923                 if(this.activePanel.afterSlide){
53924                     this.activePanel.afterSlide();
53925                 }
53926             }
53927         }
53928     },
53929
53930     initAutoHide : function(){
53931         if(this.autoHide !== false){
53932             if(!this.autoHideHd){
53933                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53934                 this.autoHideHd = {
53935                     "mouseout": function(e){
53936                         if(!e.within(this.el, true)){
53937                             st.delay(500);
53938                         }
53939                     },
53940                     "mouseover" : function(e){
53941                         st.cancel();
53942                     },
53943                     scope : this
53944                 };
53945             }
53946             this.el.on(this.autoHideHd);
53947         }
53948     },
53949
53950     clearAutoHide : function(){
53951         if(this.autoHide !== false){
53952             this.el.un("mouseout", this.autoHideHd.mouseout);
53953             this.el.un("mouseover", this.autoHideHd.mouseover);
53954         }
53955     },
53956
53957     clearMonitor : function(){
53958         Roo.get(document).un("click", this.slideInIf, this);
53959     },
53960
53961     // these names are backwards but not changed for compat
53962     slideOut : function(){
53963         if(this.isSlid || this.el.hasActiveFx()){
53964             return;
53965         }
53966         this.isSlid = true;
53967         if(this.collapseBtn){
53968             this.collapseBtn.hide();
53969         }
53970         this.closeBtnState = this.closeBtn.getStyle('display');
53971         this.closeBtn.hide();
53972         if(this.stickBtn){
53973             this.stickBtn.show();
53974         }
53975         this.el.show();
53976         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53977         this.beforeSlide();
53978         this.el.setStyle("z-index", 10001);
53979         this.el.slideIn(this.getSlideAnchor(), {
53980             callback: function(){
53981                 this.afterSlide();
53982                 this.initAutoHide();
53983                 Roo.get(document).on("click", this.slideInIf, this);
53984                 this.fireEvent("slideshow", this);
53985             },
53986             scope: this,
53987             block: true
53988         });
53989     },
53990
53991     afterSlideIn : function(){
53992         this.clearAutoHide();
53993         this.isSlid = false;
53994         this.clearMonitor();
53995         this.el.setStyle("z-index", "");
53996         if(this.collapseBtn){
53997             this.collapseBtn.show();
53998         }
53999         this.closeBtn.setStyle('display', this.closeBtnState);
54000         if(this.stickBtn){
54001             this.stickBtn.hide();
54002         }
54003         this.fireEvent("slidehide", this);
54004     },
54005
54006     slideIn : function(cb){
54007         if(!this.isSlid || this.el.hasActiveFx()){
54008             Roo.callback(cb);
54009             return;
54010         }
54011         this.isSlid = false;
54012         this.beforeSlide();
54013         this.el.slideOut(this.getSlideAnchor(), {
54014             callback: function(){
54015                 this.el.setLeftTop(-10000, -10000);
54016                 this.afterSlide();
54017                 this.afterSlideIn();
54018                 Roo.callback(cb);
54019             },
54020             scope: this,
54021             block: true
54022         });
54023     },
54024     
54025     slideInIf : function(e){
54026         if(!e.within(this.el)){
54027             this.slideIn();
54028         }
54029     },
54030
54031     animateCollapse : function(){
54032         this.beforeSlide();
54033         this.el.setStyle("z-index", 20000);
54034         var anchor = this.getSlideAnchor();
54035         this.el.slideOut(anchor, {
54036             callback : function(){
54037                 this.el.setStyle("z-index", "");
54038                 this.collapsedEl.slideIn(anchor, {duration:.3});
54039                 this.afterSlide();
54040                 this.el.setLocation(-10000,-10000);
54041                 this.el.hide();
54042                 this.fireEvent("collapsed", this);
54043             },
54044             scope: this,
54045             block: true
54046         });
54047     },
54048
54049     animateExpand : function(){
54050         this.beforeSlide();
54051         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54052         this.el.setStyle("z-index", 20000);
54053         this.collapsedEl.hide({
54054             duration:.1
54055         });
54056         this.el.slideIn(this.getSlideAnchor(), {
54057             callback : function(){
54058                 this.el.setStyle("z-index", "");
54059                 this.afterSlide();
54060                 if(this.split){
54061                     this.split.el.show();
54062                 }
54063                 this.fireEvent("invalidated", this);
54064                 this.fireEvent("expanded", this);
54065             },
54066             scope: this,
54067             block: true
54068         });
54069     },
54070
54071     anchors : {
54072         "west" : "left",
54073         "east" : "right",
54074         "north" : "top",
54075         "south" : "bottom"
54076     },
54077
54078     sanchors : {
54079         "west" : "l",
54080         "east" : "r",
54081         "north" : "t",
54082         "south" : "b"
54083     },
54084
54085     canchors : {
54086         "west" : "tl-tr",
54087         "east" : "tr-tl",
54088         "north" : "tl-bl",
54089         "south" : "bl-tl"
54090     },
54091
54092     getAnchor : function(){
54093         return this.anchors[this.position];
54094     },
54095
54096     getCollapseAnchor : function(){
54097         return this.canchors[this.position];
54098     },
54099
54100     getSlideAnchor : function(){
54101         return this.sanchors[this.position];
54102     },
54103
54104     getAlignAdj : function(){
54105         var cm = this.cmargins;
54106         switch(this.position){
54107             case "west":
54108                 return [0, 0];
54109             break;
54110             case "east":
54111                 return [0, 0];
54112             break;
54113             case "north":
54114                 return [0, 0];
54115             break;
54116             case "south":
54117                 return [0, 0];
54118             break;
54119         }
54120     },
54121
54122     getExpandAdj : function(){
54123         var c = this.collapsedEl, cm = this.cmargins;
54124         switch(this.position){
54125             case "west":
54126                 return [-(cm.right+c.getWidth()+cm.left), 0];
54127             break;
54128             case "east":
54129                 return [cm.right+c.getWidth()+cm.left, 0];
54130             break;
54131             case "north":
54132                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54133             break;
54134             case "south":
54135                 return [0, cm.top+cm.bottom+c.getHeight()];
54136             break;
54137         }
54138     }
54139 });/*
54140  * Based on:
54141  * Ext JS Library 1.1.1
54142  * Copyright(c) 2006-2007, Ext JS, LLC.
54143  *
54144  * Originally Released Under LGPL - original licence link has changed is not relivant.
54145  *
54146  * Fork - LGPL
54147  * <script type="text/javascript">
54148  */
54149 /*
54150  * These classes are private internal classes
54151  */
54152 Roo.CenterLayoutRegion = function(mgr, config){
54153     Roo.LayoutRegion.call(this, mgr, config, "center");
54154     this.visible = true;
54155     this.minWidth = config.minWidth || 20;
54156     this.minHeight = config.minHeight || 20;
54157 };
54158
54159 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54160     hide : function(){
54161         // center panel can't be hidden
54162     },
54163     
54164     show : function(){
54165         // center panel can't be hidden
54166     },
54167     
54168     getMinWidth: function(){
54169         return this.minWidth;
54170     },
54171     
54172     getMinHeight: function(){
54173         return this.minHeight;
54174     }
54175 });
54176
54177
54178 Roo.NorthLayoutRegion = function(mgr, config){
54179     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54180     if(this.split){
54181         this.split.placement = Roo.SplitBar.TOP;
54182         this.split.orientation = Roo.SplitBar.VERTICAL;
54183         this.split.el.addClass("x-layout-split-v");
54184     }
54185     var size = config.initialSize || config.height;
54186     if(typeof size != "undefined"){
54187         this.el.setHeight(size);
54188     }
54189 };
54190 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54191     orientation: Roo.SplitBar.VERTICAL,
54192     getBox : function(){
54193         if(this.collapsed){
54194             return this.collapsedEl.getBox();
54195         }
54196         var box = this.el.getBox();
54197         if(this.split){
54198             box.height += this.split.el.getHeight();
54199         }
54200         return box;
54201     },
54202     
54203     updateBox : function(box){
54204         if(this.split && !this.collapsed){
54205             box.height -= this.split.el.getHeight();
54206             this.split.el.setLeft(box.x);
54207             this.split.el.setTop(box.y+box.height);
54208             this.split.el.setWidth(box.width);
54209         }
54210         if(this.collapsed){
54211             this.updateBody(box.width, null);
54212         }
54213         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54214     }
54215 });
54216
54217 Roo.SouthLayoutRegion = function(mgr, config){
54218     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54219     if(this.split){
54220         this.split.placement = Roo.SplitBar.BOTTOM;
54221         this.split.orientation = Roo.SplitBar.VERTICAL;
54222         this.split.el.addClass("x-layout-split-v");
54223     }
54224     var size = config.initialSize || config.height;
54225     if(typeof size != "undefined"){
54226         this.el.setHeight(size);
54227     }
54228 };
54229 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54230     orientation: Roo.SplitBar.VERTICAL,
54231     getBox : function(){
54232         if(this.collapsed){
54233             return this.collapsedEl.getBox();
54234         }
54235         var box = this.el.getBox();
54236         if(this.split){
54237             var sh = this.split.el.getHeight();
54238             box.height += sh;
54239             box.y -= sh;
54240         }
54241         return box;
54242     },
54243     
54244     updateBox : function(box){
54245         if(this.split && !this.collapsed){
54246             var sh = this.split.el.getHeight();
54247             box.height -= sh;
54248             box.y += sh;
54249             this.split.el.setLeft(box.x);
54250             this.split.el.setTop(box.y-sh);
54251             this.split.el.setWidth(box.width);
54252         }
54253         if(this.collapsed){
54254             this.updateBody(box.width, null);
54255         }
54256         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54257     }
54258 });
54259
54260 Roo.EastLayoutRegion = function(mgr, config){
54261     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54262     if(this.split){
54263         this.split.placement = Roo.SplitBar.RIGHT;
54264         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54265         this.split.el.addClass("x-layout-split-h");
54266     }
54267     var size = config.initialSize || config.width;
54268     if(typeof size != "undefined"){
54269         this.el.setWidth(size);
54270     }
54271 };
54272 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54273     orientation: Roo.SplitBar.HORIZONTAL,
54274     getBox : function(){
54275         if(this.collapsed){
54276             return this.collapsedEl.getBox();
54277         }
54278         var box = this.el.getBox();
54279         if(this.split){
54280             var sw = this.split.el.getWidth();
54281             box.width += sw;
54282             box.x -= sw;
54283         }
54284         return box;
54285     },
54286
54287     updateBox : function(box){
54288         if(this.split && !this.collapsed){
54289             var sw = this.split.el.getWidth();
54290             box.width -= sw;
54291             this.split.el.setLeft(box.x);
54292             this.split.el.setTop(box.y);
54293             this.split.el.setHeight(box.height);
54294             box.x += sw;
54295         }
54296         if(this.collapsed){
54297             this.updateBody(null, box.height);
54298         }
54299         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54300     }
54301 });
54302
54303 Roo.WestLayoutRegion = function(mgr, config){
54304     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54305     if(this.split){
54306         this.split.placement = Roo.SplitBar.LEFT;
54307         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54308         this.split.el.addClass("x-layout-split-h");
54309     }
54310     var size = config.initialSize || config.width;
54311     if(typeof size != "undefined"){
54312         this.el.setWidth(size);
54313     }
54314 };
54315 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54316     orientation: Roo.SplitBar.HORIZONTAL,
54317     getBox : function(){
54318         if(this.collapsed){
54319             return this.collapsedEl.getBox();
54320         }
54321         var box = this.el.getBox();
54322         if(this.split){
54323             box.width += this.split.el.getWidth();
54324         }
54325         return box;
54326     },
54327     
54328     updateBox : function(box){
54329         if(this.split && !this.collapsed){
54330             var sw = this.split.el.getWidth();
54331             box.width -= sw;
54332             this.split.el.setLeft(box.x+box.width);
54333             this.split.el.setTop(box.y);
54334             this.split.el.setHeight(box.height);
54335         }
54336         if(this.collapsed){
54337             this.updateBody(null, box.height);
54338         }
54339         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54340     }
54341 });
54342 /*
54343  * Based on:
54344  * Ext JS Library 1.1.1
54345  * Copyright(c) 2006-2007, Ext JS, LLC.
54346  *
54347  * Originally Released Under LGPL - original licence link has changed is not relivant.
54348  *
54349  * Fork - LGPL
54350  * <script type="text/javascript">
54351  */
54352  
54353  
54354 /*
54355  * Private internal class for reading and applying state
54356  */
54357 Roo.LayoutStateManager = function(layout){
54358      // default empty state
54359      this.state = {
54360         north: {},
54361         south: {},
54362         east: {},
54363         west: {}       
54364     };
54365 };
54366
54367 Roo.LayoutStateManager.prototype = {
54368     init : function(layout, provider){
54369         this.provider = provider;
54370         var state = provider.get(layout.id+"-layout-state");
54371         if(state){
54372             var wasUpdating = layout.isUpdating();
54373             if(!wasUpdating){
54374                 layout.beginUpdate();
54375             }
54376             for(var key in state){
54377                 if(typeof state[key] != "function"){
54378                     var rstate = state[key];
54379                     var r = layout.getRegion(key);
54380                     if(r && rstate){
54381                         if(rstate.size){
54382                             r.resizeTo(rstate.size);
54383                         }
54384                         if(rstate.collapsed == true){
54385                             r.collapse(true);
54386                         }else{
54387                             r.expand(null, true);
54388                         }
54389                     }
54390                 }
54391             }
54392             if(!wasUpdating){
54393                 layout.endUpdate();
54394             }
54395             this.state = state; 
54396         }
54397         this.layout = layout;
54398         layout.on("regionresized", this.onRegionResized, this);
54399         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54400         layout.on("regionexpanded", this.onRegionExpanded, this);
54401     },
54402     
54403     storeState : function(){
54404         this.provider.set(this.layout.id+"-layout-state", this.state);
54405     },
54406     
54407     onRegionResized : function(region, newSize){
54408         this.state[region.getPosition()].size = newSize;
54409         this.storeState();
54410     },
54411     
54412     onRegionCollapsed : function(region){
54413         this.state[region.getPosition()].collapsed = true;
54414         this.storeState();
54415     },
54416     
54417     onRegionExpanded : function(region){
54418         this.state[region.getPosition()].collapsed = false;
54419         this.storeState();
54420     }
54421 };/*
54422  * Based on:
54423  * Ext JS Library 1.1.1
54424  * Copyright(c) 2006-2007, Ext JS, LLC.
54425  *
54426  * Originally Released Under LGPL - original licence link has changed is not relivant.
54427  *
54428  * Fork - LGPL
54429  * <script type="text/javascript">
54430  */
54431 /**
54432  * @class Roo.ContentPanel
54433  * @extends Roo.util.Observable
54434  * @children Roo.form.Form Roo.JsonView Roo.View
54435  * A basic ContentPanel element.
54436  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54437  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54438  * @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
54439  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54440  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54441  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54442  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
54443  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54444  * @cfg {String} title          The title for this panel
54445  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54446  * @cfg {String} url            Calls {@link #setUrl} with this value
54447  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54448  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54449  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54450  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54451  * @cfg {String}    style  Extra style to add to the content panel
54452  * @cfg {Roo.menu.Menu} menu  popup menu
54453
54454  * @constructor
54455  * Create a new ContentPanel.
54456  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54457  * @param {String/Object} config A string to set only the title or a config object
54458  * @param {String} content (optional) Set the HTML content for this panel
54459  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54460  */
54461 Roo.ContentPanel = function(el, config, content){
54462     
54463      
54464     /*
54465     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54466         config = el;
54467         el = Roo.id();
54468     }
54469     if (config && config.parentLayout) { 
54470         el = config.parentLayout.el.createChild(); 
54471     }
54472     */
54473     if(el.autoCreate){ // xtype is available if this is called from factory
54474         config = el;
54475         el = Roo.id();
54476     }
54477     this.el = Roo.get(el);
54478     if(!this.el && config && config.autoCreate){
54479         if(typeof config.autoCreate == "object"){
54480             if(!config.autoCreate.id){
54481                 config.autoCreate.id = config.id||el;
54482             }
54483             this.el = Roo.DomHelper.append(document.body,
54484                         config.autoCreate, true);
54485         }else{
54486             this.el = Roo.DomHelper.append(document.body,
54487                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54488         }
54489     }
54490     
54491     
54492     this.closable = false;
54493     this.loaded = false;
54494     this.active = false;
54495     if(typeof config == "string"){
54496         this.title = config;
54497     }else{
54498         Roo.apply(this, config);
54499     }
54500     
54501     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54502         this.wrapEl = this.el.wrap();
54503         this.toolbar.container = this.el.insertSibling(false, 'before');
54504         this.toolbar = new Roo.Toolbar(this.toolbar);
54505     }
54506     
54507     // xtype created footer. - not sure if will work as we normally have to render first..
54508     if (this.footer && !this.footer.el && this.footer.xtype) {
54509         if (!this.wrapEl) {
54510             this.wrapEl = this.el.wrap();
54511         }
54512     
54513         this.footer.container = this.wrapEl.createChild();
54514          
54515         this.footer = Roo.factory(this.footer, Roo);
54516         
54517     }
54518     
54519     if(this.resizeEl){
54520         this.resizeEl = Roo.get(this.resizeEl, true);
54521     }else{
54522         this.resizeEl = this.el;
54523     }
54524     // handle view.xtype
54525     
54526  
54527     
54528     
54529     this.addEvents({
54530         /**
54531          * @event activate
54532          * Fires when this panel is activated. 
54533          * @param {Roo.ContentPanel} this
54534          */
54535         "activate" : true,
54536         /**
54537          * @event deactivate
54538          * Fires when this panel is activated. 
54539          * @param {Roo.ContentPanel} this
54540          */
54541         "deactivate" : true,
54542
54543         /**
54544          * @event resize
54545          * Fires when this panel is resized if fitToFrame is true.
54546          * @param {Roo.ContentPanel} this
54547          * @param {Number} width The width after any component adjustments
54548          * @param {Number} height The height after any component adjustments
54549          */
54550         "resize" : true,
54551         
54552          /**
54553          * @event render
54554          * Fires when this tab is created
54555          * @param {Roo.ContentPanel} this
54556          */
54557         "render" : true
54558          
54559         
54560     });
54561     
54562
54563     
54564     
54565     if(this.autoScroll){
54566         this.resizeEl.setStyle("overflow", "auto");
54567     } else {
54568         // fix randome scrolling
54569         this.el.on('scroll', function() {
54570             Roo.log('fix random scolling');
54571             this.scrollTo('top',0); 
54572         });
54573     }
54574     content = content || this.content;
54575     if(content){
54576         this.setContent(content);
54577     }
54578     if(config && config.url){
54579         this.setUrl(this.url, this.params, this.loadOnce);
54580     }
54581     
54582     
54583     
54584     Roo.ContentPanel.superclass.constructor.call(this);
54585     
54586     if (this.view && typeof(this.view.xtype) != 'undefined') {
54587         this.view.el = this.el.appendChild(document.createElement("div"));
54588         this.view = Roo.factory(this.view); 
54589         this.view.render  &&  this.view.render(false, '');  
54590     }
54591     
54592     
54593     this.fireEvent('render', this);
54594 };
54595
54596 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54597     tabTip:'',
54598     setRegion : function(region){
54599         this.region = region;
54600         if(region){
54601            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54602         }else{
54603            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54604         } 
54605     },
54606     
54607     /**
54608      * Returns the toolbar for this Panel if one was configured. 
54609      * @return {Roo.Toolbar} 
54610      */
54611     getToolbar : function(){
54612         return this.toolbar;
54613     },
54614     
54615     setActiveState : function(active){
54616         this.active = active;
54617         if(!active){
54618             this.fireEvent("deactivate", this);
54619         }else{
54620             this.fireEvent("activate", this);
54621         }
54622     },
54623     /**
54624      * Updates this panel's element
54625      * @param {String} content The new content
54626      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54627     */
54628     setContent : function(content, loadScripts){
54629         this.el.update(content, loadScripts);
54630     },
54631
54632     ignoreResize : function(w, h){
54633         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54634             return true;
54635         }else{
54636             this.lastSize = {width: w, height: h};
54637             return false;
54638         }
54639     },
54640     /**
54641      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54642      * @return {Roo.UpdateManager} The UpdateManager
54643      */
54644     getUpdateManager : function(){
54645         return this.el.getUpdateManager();
54646     },
54647      /**
54648      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54649      * @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:
54650 <pre><code>
54651 panel.load({
54652     url: "your-url.php",
54653     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54654     callback: yourFunction,
54655     scope: yourObject, //(optional scope)
54656     discardUrl: false,
54657     nocache: false,
54658     text: "Loading...",
54659     timeout: 30,
54660     scripts: false
54661 });
54662 </code></pre>
54663      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54664      * 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.
54665      * @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}
54666      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54667      * @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.
54668      * @return {Roo.ContentPanel} this
54669      */
54670     load : function(){
54671         var um = this.el.getUpdateManager();
54672         um.update.apply(um, arguments);
54673         return this;
54674     },
54675
54676
54677     /**
54678      * 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.
54679      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54680      * @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)
54681      * @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)
54682      * @return {Roo.UpdateManager} The UpdateManager
54683      */
54684     setUrl : function(url, params, loadOnce){
54685         if(this.refreshDelegate){
54686             this.removeListener("activate", this.refreshDelegate);
54687         }
54688         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54689         this.on("activate", this.refreshDelegate);
54690         return this.el.getUpdateManager();
54691     },
54692     
54693     _handleRefresh : function(url, params, loadOnce){
54694         if(!loadOnce || !this.loaded){
54695             var updater = this.el.getUpdateManager();
54696             updater.update(url, params, this._setLoaded.createDelegate(this));
54697         }
54698     },
54699     
54700     _setLoaded : function(){
54701         this.loaded = true;
54702     }, 
54703     
54704     /**
54705      * Returns this panel's id
54706      * @return {String} 
54707      */
54708     getId : function(){
54709         return this.el.id;
54710     },
54711     
54712     /** 
54713      * Returns this panel's element - used by regiosn to add.
54714      * @return {Roo.Element} 
54715      */
54716     getEl : function(){
54717         return this.wrapEl || this.el;
54718     },
54719     
54720     adjustForComponents : function(width, height)
54721     {
54722         //Roo.log('adjustForComponents ');
54723         if(this.resizeEl != this.el){
54724             width -= this.el.getFrameWidth('lr');
54725             height -= this.el.getFrameWidth('tb');
54726         }
54727         if(this.toolbar){
54728             var te = this.toolbar.getEl();
54729             height -= te.getHeight();
54730             te.setWidth(width);
54731         }
54732         if(this.footer){
54733             var te = this.footer.getEl();
54734             //Roo.log("footer:" + te.getHeight());
54735             
54736             height -= te.getHeight();
54737             te.setWidth(width);
54738         }
54739         
54740         
54741         if(this.adjustments){
54742             width += this.adjustments[0];
54743             height += this.adjustments[1];
54744         }
54745         return {"width": width, "height": height};
54746     },
54747     
54748     setSize : function(width, height){
54749         if(this.fitToFrame && !this.ignoreResize(width, height)){
54750             if(this.fitContainer && this.resizeEl != this.el){
54751                 this.el.setSize(width, height);
54752             }
54753             var size = this.adjustForComponents(width, height);
54754             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54755             this.fireEvent('resize', this, size.width, size.height);
54756         }
54757     },
54758     
54759     /**
54760      * Returns this panel's title
54761      * @return {String} 
54762      */
54763     getTitle : function(){
54764         return this.title;
54765     },
54766     
54767     /**
54768      * Set this panel's title
54769      * @param {String} title
54770      */
54771     setTitle : function(title){
54772         this.title = title;
54773         if(this.region){
54774             this.region.updatePanelTitle(this, title);
54775         }
54776     },
54777     
54778     /**
54779      * Returns true is this panel was configured to be closable
54780      * @return {Boolean} 
54781      */
54782     isClosable : function(){
54783         return this.closable;
54784     },
54785     
54786     beforeSlide : function(){
54787         this.el.clip();
54788         this.resizeEl.clip();
54789     },
54790     
54791     afterSlide : function(){
54792         this.el.unclip();
54793         this.resizeEl.unclip();
54794     },
54795     
54796     /**
54797      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54798      *   Will fail silently if the {@link #setUrl} method has not been called.
54799      *   This does not activate the panel, just updates its content.
54800      */
54801     refresh : function(){
54802         if(this.refreshDelegate){
54803            this.loaded = false;
54804            this.refreshDelegate();
54805         }
54806     },
54807     
54808     /**
54809      * Destroys this panel
54810      */
54811     destroy : function(){
54812         this.el.removeAllListeners();
54813         var tempEl = document.createElement("span");
54814         tempEl.appendChild(this.el.dom);
54815         tempEl.innerHTML = "";
54816         this.el.remove();
54817         this.el = null;
54818     },
54819     
54820     /**
54821      * form - if the content panel contains a form - this is a reference to it.
54822      * @type {Roo.form.Form}
54823      */
54824     form : false,
54825     /**
54826      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54827      *    This contains a reference to it.
54828      * @type {Roo.View}
54829      */
54830     view : false,
54831     
54832       /**
54833      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54834      * <pre><code>
54835
54836 layout.addxtype({
54837        xtype : 'Form',
54838        items: [ .... ]
54839    }
54840 );
54841
54842 </code></pre>
54843      * @param {Object} cfg Xtype definition of item to add.
54844      */
54845     
54846     addxtype : function(cfg) {
54847         // add form..
54848         if (cfg.xtype.match(/^Form$/)) {
54849             
54850             var el;
54851             //if (this.footer) {
54852             //    el = this.footer.container.insertSibling(false, 'before');
54853             //} else {
54854                 el = this.el.createChild();
54855             //}
54856
54857             this.form = new  Roo.form.Form(cfg);
54858             
54859             
54860             if ( this.form.allItems.length) {
54861                 this.form.render(el.dom);
54862             }
54863             return this.form;
54864         }
54865         // should only have one of theses..
54866         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54867             // views.. should not be just added - used named prop 'view''
54868             
54869             cfg.el = this.el.appendChild(document.createElement("div"));
54870             // factory?
54871             
54872             var ret = new Roo.factory(cfg);
54873              
54874              ret.render && ret.render(false, ''); // render blank..
54875             this.view = ret;
54876             return ret;
54877         }
54878         return false;
54879     }
54880 });
54881
54882 /**
54883  * @class Roo.GridPanel
54884  * @extends Roo.ContentPanel
54885  * @constructor
54886  * Create a new GridPanel.
54887  * @param {Roo.grid.Grid} grid The grid for this panel
54888  * @param {String/Object} config A string to set only the panel's title, or a config object
54889  */
54890 Roo.GridPanel = function(grid, config){
54891     
54892   
54893     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54894         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54895         
54896     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54897     
54898     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54899     
54900     if(this.toolbar){
54901         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54902     }
54903     // xtype created footer. - not sure if will work as we normally have to render first..
54904     if (this.footer && !this.footer.el && this.footer.xtype) {
54905         
54906         this.footer.container = this.grid.getView().getFooterPanel(true);
54907         this.footer.dataSource = this.grid.dataSource;
54908         this.footer = Roo.factory(this.footer, Roo);
54909         
54910     }
54911     
54912     grid.monitorWindowResize = false; // turn off autosizing
54913     grid.autoHeight = false;
54914     grid.autoWidth = false;
54915     this.grid = grid;
54916     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54917 };
54918
54919 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54920     getId : function(){
54921         return this.grid.id;
54922     },
54923     
54924     /**
54925      * Returns the grid for this panel
54926      * @return {Roo.grid.Grid} 
54927      */
54928     getGrid : function(){
54929         return this.grid;    
54930     },
54931     
54932     setSize : function(width, height){
54933         if(!this.ignoreResize(width, height)){
54934             var grid = this.grid;
54935             var size = this.adjustForComponents(width, height);
54936             grid.getGridEl().setSize(size.width, size.height);
54937             grid.autoSize();
54938         }
54939     },
54940     
54941     beforeSlide : function(){
54942         this.grid.getView().scroller.clip();
54943     },
54944     
54945     afterSlide : function(){
54946         this.grid.getView().scroller.unclip();
54947     },
54948     
54949     destroy : function(){
54950         this.grid.destroy();
54951         delete this.grid;
54952         Roo.GridPanel.superclass.destroy.call(this); 
54953     }
54954 });
54955
54956
54957 /**
54958  * @class Roo.NestedLayoutPanel
54959  * @extends Roo.ContentPanel
54960  * @constructor
54961  * Create a new NestedLayoutPanel.
54962  * 
54963  * 
54964  * @param {Roo.BorderLayout} layout [required] The layout for this panel
54965  * @param {String/Object} config A string to set only the title or a config object
54966  */
54967 Roo.NestedLayoutPanel = function(layout, config)
54968 {
54969     // construct with only one argument..
54970     /* FIXME - implement nicer consturctors
54971     if (layout.layout) {
54972         config = layout;
54973         layout = config.layout;
54974         delete config.layout;
54975     }
54976     if (layout.xtype && !layout.getEl) {
54977         // then layout needs constructing..
54978         layout = Roo.factory(layout, Roo);
54979     }
54980     */
54981     
54982     
54983     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54984     
54985     layout.monitorWindowResize = false; // turn off autosizing
54986     this.layout = layout;
54987     this.layout.getEl().addClass("x-layout-nested-layout");
54988     
54989     
54990     
54991     
54992 };
54993
54994 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54995
54996     setSize : function(width, height){
54997         if(!this.ignoreResize(width, height)){
54998             var size = this.adjustForComponents(width, height);
54999             var el = this.layout.getEl();
55000             el.setSize(size.width, size.height);
55001             var touch = el.dom.offsetWidth;
55002             this.layout.layout();
55003             // ie requires a double layout on the first pass
55004             if(Roo.isIE && !this.initialized){
55005                 this.initialized = true;
55006                 this.layout.layout();
55007             }
55008         }
55009     },
55010     
55011     // activate all subpanels if not currently active..
55012     
55013     setActiveState : function(active){
55014         this.active = active;
55015         if(!active){
55016             this.fireEvent("deactivate", this);
55017             return;
55018         }
55019         
55020         this.fireEvent("activate", this);
55021         // not sure if this should happen before or after..
55022         if (!this.layout) {
55023             return; // should not happen..
55024         }
55025         var reg = false;
55026         for (var r in this.layout.regions) {
55027             reg = this.layout.getRegion(r);
55028             if (reg.getActivePanel()) {
55029                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55030                 reg.setActivePanel(reg.getActivePanel());
55031                 continue;
55032             }
55033             if (!reg.panels.length) {
55034                 continue;
55035             }
55036             reg.showPanel(reg.getPanel(0));
55037         }
55038         
55039         
55040         
55041         
55042     },
55043     
55044     /**
55045      * Returns the nested BorderLayout for this panel
55046      * @return {Roo.BorderLayout} 
55047      */
55048     getLayout : function(){
55049         return this.layout;
55050     },
55051     
55052      /**
55053      * Adds a xtype elements to the layout of the nested panel
55054      * <pre><code>
55055
55056 panel.addxtype({
55057        xtype : 'ContentPanel',
55058        region: 'west',
55059        items: [ .... ]
55060    }
55061 );
55062
55063 panel.addxtype({
55064         xtype : 'NestedLayoutPanel',
55065         region: 'west',
55066         layout: {
55067            center: { },
55068            west: { }   
55069         },
55070         items : [ ... list of content panels or nested layout panels.. ]
55071    }
55072 );
55073 </code></pre>
55074      * @param {Object} cfg Xtype definition of item to add.
55075      */
55076     addxtype : function(cfg) {
55077         return this.layout.addxtype(cfg);
55078     
55079     }
55080 });
55081
55082 Roo.ScrollPanel = function(el, config, content){
55083     config = config || {};
55084     config.fitToFrame = true;
55085     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55086     
55087     this.el.dom.style.overflow = "hidden";
55088     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55089     this.el.removeClass("x-layout-inactive-content");
55090     this.el.on("mousewheel", this.onWheel, this);
55091
55092     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55093     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55094     up.unselectable(); down.unselectable();
55095     up.on("click", this.scrollUp, this);
55096     down.on("click", this.scrollDown, this);
55097     up.addClassOnOver("x-scroller-btn-over");
55098     down.addClassOnOver("x-scroller-btn-over");
55099     up.addClassOnClick("x-scroller-btn-click");
55100     down.addClassOnClick("x-scroller-btn-click");
55101     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55102
55103     this.resizeEl = this.el;
55104     this.el = wrap; this.up = up; this.down = down;
55105 };
55106
55107 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55108     increment : 100,
55109     wheelIncrement : 5,
55110     scrollUp : function(){
55111         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55112     },
55113
55114     scrollDown : function(){
55115         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55116     },
55117
55118     afterScroll : function(){
55119         var el = this.resizeEl;
55120         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55121         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55122         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55123     },
55124
55125     setSize : function(){
55126         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55127         this.afterScroll();
55128     },
55129
55130     onWheel : function(e){
55131         var d = e.getWheelDelta();
55132         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55133         this.afterScroll();
55134         e.stopEvent();
55135     },
55136
55137     setContent : function(content, loadScripts){
55138         this.resizeEl.update(content, loadScripts);
55139     }
55140
55141 });
55142
55143
55144
55145 /**
55146  * @class Roo.TreePanel
55147  * @extends Roo.ContentPanel
55148  * Treepanel component
55149  * 
55150  * @constructor
55151  * Create a new TreePanel. - defaults to fit/scoll contents.
55152  * @param {String/Object} config A string to set only the panel's title, or a config object
55153  */
55154 Roo.TreePanel = function(config){
55155     var el = config.el;
55156     var tree = config.tree;
55157     delete config.tree; 
55158     delete config.el; // hopefull!
55159     
55160     // wrapper for IE7 strict & safari scroll issue
55161     
55162     var treeEl = el.createChild();
55163     config.resizeEl = treeEl;
55164     
55165     
55166     
55167     Roo.TreePanel.superclass.constructor.call(this, el, config);
55168  
55169  
55170     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55171     //console.log(tree);
55172     this.on('activate', function()
55173     {
55174         if (this.tree.rendered) {
55175             return;
55176         }
55177         //console.log('render tree');
55178         this.tree.render();
55179     });
55180     // this should not be needed.. - it's actually the 'el' that resizes?
55181     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55182     
55183     //this.on('resize',  function (cp, w, h) {
55184     //        this.tree.innerCt.setWidth(w);
55185     //        this.tree.innerCt.setHeight(h);
55186     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55187     //});
55188
55189         
55190     
55191 };
55192
55193 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55194     fitToFrame : true,
55195     autoScroll : true,
55196     /*
55197      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55198      */
55199     tree : false
55200
55201 });
55202
55203
55204
55205
55206
55207
55208
55209
55210
55211
55212
55213 /*
55214  * Based on:
55215  * Ext JS Library 1.1.1
55216  * Copyright(c) 2006-2007, Ext JS, LLC.
55217  *
55218  * Originally Released Under LGPL - original licence link has changed is not relivant.
55219  *
55220  * Fork - LGPL
55221  * <script type="text/javascript">
55222  */
55223  
55224
55225 /**
55226  * @class Roo.ReaderLayout
55227  * @extends Roo.BorderLayout
55228  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55229  * center region containing two nested regions (a top one for a list view and one for item preview below),
55230  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55231  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55232  * expedites the setup of the overall layout and regions for this common application style.
55233  * Example:
55234  <pre><code>
55235 var reader = new Roo.ReaderLayout();
55236 var CP = Roo.ContentPanel;  // shortcut for adding
55237
55238 reader.beginUpdate();
55239 reader.add("north", new CP("north", "North"));
55240 reader.add("west", new CP("west", {title: "West"}));
55241 reader.add("east", new CP("east", {title: "East"}));
55242
55243 reader.regions.listView.add(new CP("listView", "List"));
55244 reader.regions.preview.add(new CP("preview", "Preview"));
55245 reader.endUpdate();
55246 </code></pre>
55247 * @constructor
55248 * Create a new ReaderLayout
55249 * @param {Object} config Configuration options
55250 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55251 * document.body if omitted)
55252 */
55253 Roo.ReaderLayout = function(config, renderTo){
55254     var c = config || {size:{}};
55255     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55256         north: c.north !== false ? Roo.apply({
55257             split:false,
55258             initialSize: 32,
55259             titlebar: false
55260         }, c.north) : false,
55261         west: c.west !== false ? Roo.apply({
55262             split:true,
55263             initialSize: 200,
55264             minSize: 175,
55265             maxSize: 400,
55266             titlebar: true,
55267             collapsible: true,
55268             animate: true,
55269             margins:{left:5,right:0,bottom:5,top:5},
55270             cmargins:{left:5,right:5,bottom:5,top:5}
55271         }, c.west) : false,
55272         east: c.east !== false ? Roo.apply({
55273             split:true,
55274             initialSize: 200,
55275             minSize: 175,
55276             maxSize: 400,
55277             titlebar: true,
55278             collapsible: true,
55279             animate: true,
55280             margins:{left:0,right:5,bottom:5,top:5},
55281             cmargins:{left:5,right:5,bottom:5,top:5}
55282         }, c.east) : false,
55283         center: Roo.apply({
55284             tabPosition: 'top',
55285             autoScroll:false,
55286             closeOnTab: true,
55287             titlebar:false,
55288             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55289         }, c.center)
55290     });
55291
55292     this.el.addClass('x-reader');
55293
55294     this.beginUpdate();
55295
55296     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55297         south: c.preview !== false ? Roo.apply({
55298             split:true,
55299             initialSize: 200,
55300             minSize: 100,
55301             autoScroll:true,
55302             collapsible:true,
55303             titlebar: true,
55304             cmargins:{top:5,left:0, right:0, bottom:0}
55305         }, c.preview) : false,
55306         center: Roo.apply({
55307             autoScroll:false,
55308             titlebar:false,
55309             minHeight:200
55310         }, c.listView)
55311     });
55312     this.add('center', new Roo.NestedLayoutPanel(inner,
55313             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55314
55315     this.endUpdate();
55316
55317     this.regions.preview = inner.getRegion('south');
55318     this.regions.listView = inner.getRegion('center');
55319 };
55320
55321 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55322  * Based on:
55323  * Ext JS Library 1.1.1
55324  * Copyright(c) 2006-2007, Ext JS, LLC.
55325  *
55326  * Originally Released Under LGPL - original licence link has changed is not relivant.
55327  *
55328  * Fork - LGPL
55329  * <script type="text/javascript">
55330  */
55331  
55332 /**
55333  * @class Roo.grid.Grid
55334  * @extends Roo.util.Observable
55335  * This class represents the primary interface of a component based grid control.
55336  * <br><br>Usage:<pre><code>
55337  var grid = new Roo.grid.Grid("my-container-id", {
55338      ds: myDataStore,
55339      cm: myColModel,
55340      selModel: mySelectionModel,
55341      autoSizeColumns: true,
55342      monitorWindowResize: false,
55343      trackMouseOver: true
55344  });
55345  // set any options
55346  grid.render();
55347  * </code></pre>
55348  * <b>Common Problems:</b><br/>
55349  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55350  * element will correct this<br/>
55351  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55352  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55353  * are unpredictable.<br/>
55354  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55355  * grid to calculate dimensions/offsets.<br/>
55356   * @constructor
55357  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55358  * The container MUST have some type of size defined for the grid to fill. The container will be
55359  * automatically set to position relative if it isn't already.
55360  * @param {Object} config A config object that sets properties on this grid.
55361  */
55362 Roo.grid.Grid = function(container, config){
55363         // initialize the container
55364         this.container = Roo.get(container);
55365         this.container.update("");
55366         this.container.setStyle("overflow", "hidden");
55367     this.container.addClass('x-grid-container');
55368
55369     this.id = this.container.id;
55370
55371     Roo.apply(this, config);
55372     // check and correct shorthanded configs
55373     if(this.ds){
55374         this.dataSource = this.ds;
55375         delete this.ds;
55376     }
55377     if(this.cm){
55378         this.colModel = this.cm;
55379         delete this.cm;
55380     }
55381     if(this.sm){
55382         this.selModel = this.sm;
55383         delete this.sm;
55384     }
55385
55386     if (this.selModel) {
55387         this.selModel = Roo.factory(this.selModel, Roo.grid);
55388         this.sm = this.selModel;
55389         this.sm.xmodule = this.xmodule || false;
55390     }
55391     if (typeof(this.colModel.config) == 'undefined') {
55392         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55393         this.cm = this.colModel;
55394         this.cm.xmodule = this.xmodule || false;
55395     }
55396     if (this.dataSource) {
55397         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55398         this.ds = this.dataSource;
55399         this.ds.xmodule = this.xmodule || false;
55400          
55401     }
55402     
55403     
55404     
55405     if(this.width){
55406         this.container.setWidth(this.width);
55407     }
55408
55409     if(this.height){
55410         this.container.setHeight(this.height);
55411     }
55412     /** @private */
55413         this.addEvents({
55414         // raw events
55415         /**
55416          * @event click
55417          * The raw click event for the entire grid.
55418          * @param {Roo.EventObject} e
55419          */
55420         "click" : true,
55421         /**
55422          * @event dblclick
55423          * The raw dblclick event for the entire grid.
55424          * @param {Roo.EventObject} e
55425          */
55426         "dblclick" : true,
55427         /**
55428          * @event contextmenu
55429          * The raw contextmenu event for the entire grid.
55430          * @param {Roo.EventObject} e
55431          */
55432         "contextmenu" : true,
55433         /**
55434          * @event mousedown
55435          * The raw mousedown event for the entire grid.
55436          * @param {Roo.EventObject} e
55437          */
55438         "mousedown" : true,
55439         /**
55440          * @event mouseup
55441          * The raw mouseup event for the entire grid.
55442          * @param {Roo.EventObject} e
55443          */
55444         "mouseup" : true,
55445         /**
55446          * @event mouseover
55447          * The raw mouseover event for the entire grid.
55448          * @param {Roo.EventObject} e
55449          */
55450         "mouseover" : true,
55451         /**
55452          * @event mouseout
55453          * The raw mouseout event for the entire grid.
55454          * @param {Roo.EventObject} e
55455          */
55456         "mouseout" : true,
55457         /**
55458          * @event keypress
55459          * The raw keypress event for the entire grid.
55460          * @param {Roo.EventObject} e
55461          */
55462         "keypress" : true,
55463         /**
55464          * @event keydown
55465          * The raw keydown event for the entire grid.
55466          * @param {Roo.EventObject} e
55467          */
55468         "keydown" : true,
55469
55470         // custom events
55471
55472         /**
55473          * @event cellclick
55474          * Fires when a cell is clicked
55475          * @param {Grid} this
55476          * @param {Number} rowIndex
55477          * @param {Number} columnIndex
55478          * @param {Roo.EventObject} e
55479          */
55480         "cellclick" : true,
55481         /**
55482          * @event celldblclick
55483          * Fires when a cell is double clicked
55484          * @param {Grid} this
55485          * @param {Number} rowIndex
55486          * @param {Number} columnIndex
55487          * @param {Roo.EventObject} e
55488          */
55489         "celldblclick" : true,
55490         /**
55491          * @event rowclick
55492          * Fires when a row is clicked
55493          * @param {Grid} this
55494          * @param {Number} rowIndex
55495          * @param {Roo.EventObject} e
55496          */
55497         "rowclick" : true,
55498         /**
55499          * @event rowdblclick
55500          * Fires when a row is double clicked
55501          * @param {Grid} this
55502          * @param {Number} rowIndex
55503          * @param {Roo.EventObject} e
55504          */
55505         "rowdblclick" : true,
55506         /**
55507          * @event headerclick
55508          * Fires when a header is clicked
55509          * @param {Grid} this
55510          * @param {Number} columnIndex
55511          * @param {Roo.EventObject} e
55512          */
55513         "headerclick" : true,
55514         /**
55515          * @event headerdblclick
55516          * Fires when a header cell is double clicked
55517          * @param {Grid} this
55518          * @param {Number} columnIndex
55519          * @param {Roo.EventObject} e
55520          */
55521         "headerdblclick" : true,
55522         /**
55523          * @event rowcontextmenu
55524          * Fires when a row is right clicked
55525          * @param {Grid} this
55526          * @param {Number} rowIndex
55527          * @param {Roo.EventObject} e
55528          */
55529         "rowcontextmenu" : true,
55530         /**
55531          * @event cellcontextmenu
55532          * Fires when a cell is right clicked
55533          * @param {Grid} this
55534          * @param {Number} rowIndex
55535          * @param {Number} cellIndex
55536          * @param {Roo.EventObject} e
55537          */
55538          "cellcontextmenu" : true,
55539         /**
55540          * @event headercontextmenu
55541          * Fires when a header is right clicked
55542          * @param {Grid} this
55543          * @param {Number} columnIndex
55544          * @param {Roo.EventObject} e
55545          */
55546         "headercontextmenu" : true,
55547         /**
55548          * @event bodyscroll
55549          * Fires when the body element is scrolled
55550          * @param {Number} scrollLeft
55551          * @param {Number} scrollTop
55552          */
55553         "bodyscroll" : true,
55554         /**
55555          * @event columnresize
55556          * Fires when the user resizes a column
55557          * @param {Number} columnIndex
55558          * @param {Number} newSize
55559          */
55560         "columnresize" : true,
55561         /**
55562          * @event columnmove
55563          * Fires when the user moves a column
55564          * @param {Number} oldIndex
55565          * @param {Number} newIndex
55566          */
55567         "columnmove" : true,
55568         /**
55569          * @event startdrag
55570          * Fires when row(s) start being dragged
55571          * @param {Grid} this
55572          * @param {Roo.GridDD} dd The drag drop object
55573          * @param {event} e The raw browser event
55574          */
55575         "startdrag" : true,
55576         /**
55577          * @event enddrag
55578          * Fires when a drag operation is complete
55579          * @param {Grid} this
55580          * @param {Roo.GridDD} dd The drag drop object
55581          * @param {event} e The raw browser event
55582          */
55583         "enddrag" : true,
55584         /**
55585          * @event dragdrop
55586          * Fires when dragged row(s) are dropped on a valid DD target
55587          * @param {Grid} this
55588          * @param {Roo.GridDD} dd The drag drop object
55589          * @param {String} targetId The target drag drop object
55590          * @param {event} e The raw browser event
55591          */
55592         "dragdrop" : true,
55593         /**
55594          * @event dragover
55595          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55596          * @param {Grid} this
55597          * @param {Roo.GridDD} dd The drag drop object
55598          * @param {String} targetId The target drag drop object
55599          * @param {event} e The raw browser event
55600          */
55601         "dragover" : true,
55602         /**
55603          * @event dragenter
55604          *  Fires when the dragged row(s) first cross another DD target while being dragged
55605          * @param {Grid} this
55606          * @param {Roo.GridDD} dd The drag drop object
55607          * @param {String} targetId The target drag drop object
55608          * @param {event} e The raw browser event
55609          */
55610         "dragenter" : true,
55611         /**
55612          * @event dragout
55613          * Fires when the dragged row(s) leave another DD target while being dragged
55614          * @param {Grid} this
55615          * @param {Roo.GridDD} dd The drag drop object
55616          * @param {String} targetId The target drag drop object
55617          * @param {event} e The raw browser event
55618          */
55619         "dragout" : true,
55620         /**
55621          * @event rowclass
55622          * Fires when a row is rendered, so you can change add a style to it.
55623          * @param {GridView} gridview   The grid view
55624          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55625          */
55626         'rowclass' : true,
55627
55628         /**
55629          * @event render
55630          * Fires when the grid is rendered
55631          * @param {Grid} grid
55632          */
55633         'render' : true
55634     });
55635
55636     Roo.grid.Grid.superclass.constructor.call(this);
55637 };
55638 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55639     
55640     /**
55641          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
55642          */
55643         /**
55644          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
55645          */
55646         /**
55647          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
55648          */
55649         /**
55650          * @cfg {Roo.grid.Store} ds The data store for the grid
55651          */
55652         /**
55653          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
55654          */
55655         /**
55656      * @cfg {String} ddGroup - drag drop group.
55657      */
55658       /**
55659      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55660      */
55661
55662     /**
55663      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55664      */
55665     minColumnWidth : 25,
55666
55667     /**
55668      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55669      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55670      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55671      */
55672     autoSizeColumns : false,
55673
55674     /**
55675      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55676      */
55677     autoSizeHeaders : true,
55678
55679     /**
55680      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55681      */
55682     monitorWindowResize : true,
55683
55684     /**
55685      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55686      * rows measured to get a columns size. Default is 0 (all rows).
55687      */
55688     maxRowsToMeasure : 0,
55689
55690     /**
55691      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55692      */
55693     trackMouseOver : true,
55694
55695     /**
55696     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55697     */
55698       /**
55699     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55700     */
55701     
55702     /**
55703     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55704     */
55705     enableDragDrop : false,
55706     
55707     /**
55708     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55709     */
55710     enableColumnMove : true,
55711     
55712     /**
55713     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55714     */
55715     enableColumnHide : true,
55716     
55717     /**
55718     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55719     */
55720     enableRowHeightSync : false,
55721     
55722     /**
55723     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55724     */
55725     stripeRows : true,
55726     
55727     /**
55728     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55729     */
55730     autoHeight : false,
55731
55732     /**
55733      * @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.
55734      */
55735     autoExpandColumn : false,
55736
55737     /**
55738     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55739     * Default is 50.
55740     */
55741     autoExpandMin : 50,
55742
55743     /**
55744     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55745     */
55746     autoExpandMax : 1000,
55747
55748     /**
55749     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55750     */
55751     view : null,
55752
55753     /**
55754     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55755     */
55756     loadMask : false,
55757     /**
55758     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55759     */
55760     dropTarget: false,
55761     
55762    
55763     
55764     // private
55765     rendered : false,
55766
55767     /**
55768     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55769     * of a fixed width. Default is false.
55770     */
55771     /**
55772     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55773     */
55774     
55775     
55776     /**
55777     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55778     * %0 is replaced with the number of selected rows.
55779     */
55780     ddText : "{0} selected row{1}",
55781     
55782     
55783     /**
55784      * Called once after all setup has been completed and the grid is ready to be rendered.
55785      * @return {Roo.grid.Grid} this
55786      */
55787     render : function()
55788     {
55789         var c = this.container;
55790         // try to detect autoHeight/width mode
55791         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55792             this.autoHeight = true;
55793         }
55794         var view = this.getView();
55795         view.init(this);
55796
55797         c.on("click", this.onClick, this);
55798         c.on("dblclick", this.onDblClick, this);
55799         c.on("contextmenu", this.onContextMenu, this);
55800         c.on("keydown", this.onKeyDown, this);
55801         if (Roo.isTouch) {
55802             c.on("touchstart", this.onTouchStart, this);
55803         }
55804
55805         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55806
55807         this.getSelectionModel().init(this);
55808
55809         view.render();
55810
55811         if(this.loadMask){
55812             this.loadMask = new Roo.LoadMask(this.container,
55813                     Roo.apply({store:this.dataSource}, this.loadMask));
55814         }
55815         
55816         
55817         if (this.toolbar && this.toolbar.xtype) {
55818             this.toolbar.container = this.getView().getHeaderPanel(true);
55819             this.toolbar = new Roo.Toolbar(this.toolbar);
55820         }
55821         if (this.footer && this.footer.xtype) {
55822             this.footer.dataSource = this.getDataSource();
55823             this.footer.container = this.getView().getFooterPanel(true);
55824             this.footer = Roo.factory(this.footer, Roo);
55825         }
55826         if (this.dropTarget && this.dropTarget.xtype) {
55827             delete this.dropTarget.xtype;
55828             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55829         }
55830         
55831         
55832         this.rendered = true;
55833         this.fireEvent('render', this);
55834         return this;
55835     },
55836
55837     /**
55838      * Reconfigures the grid to use a different Store and Column Model.
55839      * The View will be bound to the new objects and refreshed.
55840      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55841      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55842      */
55843     reconfigure : function(dataSource, colModel){
55844         if(this.loadMask){
55845             this.loadMask.destroy();
55846             this.loadMask = new Roo.LoadMask(this.container,
55847                     Roo.apply({store:dataSource}, this.loadMask));
55848         }
55849         this.view.bind(dataSource, colModel);
55850         this.dataSource = dataSource;
55851         this.colModel = colModel;
55852         this.view.refresh(true);
55853     },
55854     /**
55855      * addColumns
55856      * Add's a column, default at the end..
55857      
55858      * @param {int} position to add (default end)
55859      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55860      */
55861     addColumns : function(pos, ar)
55862     {
55863         
55864         for (var i =0;i< ar.length;i++) {
55865             var cfg = ar[i];
55866             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55867             this.cm.lookup[cfg.id] = cfg;
55868         }
55869         
55870         
55871         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55872             pos = this.cm.config.length; //this.cm.config.push(cfg);
55873         } 
55874         pos = Math.max(0,pos);
55875         ar.unshift(0);
55876         ar.unshift(pos);
55877         this.cm.config.splice.apply(this.cm.config, ar);
55878         
55879         
55880         
55881         this.view.generateRules(this.cm);
55882         this.view.refresh(true);
55883         
55884     },
55885     
55886     
55887     
55888     
55889     // private
55890     onKeyDown : function(e){
55891         this.fireEvent("keydown", e);
55892     },
55893
55894     /**
55895      * Destroy this grid.
55896      * @param {Boolean} removeEl True to remove the element
55897      */
55898     destroy : function(removeEl, keepListeners){
55899         if(this.loadMask){
55900             this.loadMask.destroy();
55901         }
55902         var c = this.container;
55903         c.removeAllListeners();
55904         this.view.destroy();
55905         this.colModel.purgeListeners();
55906         if(!keepListeners){
55907             this.purgeListeners();
55908         }
55909         c.update("");
55910         if(removeEl === true){
55911             c.remove();
55912         }
55913     },
55914
55915     // private
55916     processEvent : function(name, e){
55917         // does this fire select???
55918         //Roo.log('grid:processEvent '  + name);
55919         
55920         if (name != 'touchstart' ) {
55921             this.fireEvent(name, e);    
55922         }
55923         
55924         var t = e.getTarget();
55925         var v = this.view;
55926         var header = v.findHeaderIndex(t);
55927         if(header !== false){
55928             var ename = name == 'touchstart' ? 'click' : name;
55929              
55930             this.fireEvent("header" + ename, this, header, e);
55931         }else{
55932             var row = v.findRowIndex(t);
55933             var cell = v.findCellIndex(t);
55934             if (name == 'touchstart') {
55935                 // first touch is always a click.
55936                 // hopefull this happens after selection is updated.?
55937                 name = false;
55938                 
55939                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55940                     var cs = this.selModel.getSelectedCell();
55941                     if (row == cs[0] && cell == cs[1]){
55942                         name = 'dblclick';
55943                     }
55944                 }
55945                 if (typeof(this.selModel.getSelections) != 'undefined') {
55946                     var cs = this.selModel.getSelections();
55947                     var ds = this.dataSource;
55948                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55949                         name = 'dblclick';
55950                     }
55951                 }
55952                 if (!name) {
55953                     return;
55954                 }
55955             }
55956             
55957             
55958             if(row !== false){
55959                 this.fireEvent("row" + name, this, row, e);
55960                 if(cell !== false){
55961                     this.fireEvent("cell" + name, this, row, cell, e);
55962                 }
55963             }
55964         }
55965     },
55966
55967     // private
55968     onClick : function(e){
55969         this.processEvent("click", e);
55970     },
55971    // private
55972     onTouchStart : function(e){
55973         this.processEvent("touchstart", e);
55974     },
55975
55976     // private
55977     onContextMenu : function(e, t){
55978         this.processEvent("contextmenu", e);
55979     },
55980
55981     // private
55982     onDblClick : function(e){
55983         this.processEvent("dblclick", e);
55984     },
55985
55986     // private
55987     walkCells : function(row, col, step, fn, scope){
55988         var cm = this.colModel, clen = cm.getColumnCount();
55989         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55990         if(step < 0){
55991             if(col < 0){
55992                 row--;
55993                 first = false;
55994             }
55995             while(row >= 0){
55996                 if(!first){
55997                     col = clen-1;
55998                 }
55999                 first = false;
56000                 while(col >= 0){
56001                     if(fn.call(scope || this, row, col, cm) === true){
56002                         return [row, col];
56003                     }
56004                     col--;
56005                 }
56006                 row--;
56007             }
56008         } else {
56009             if(col >= clen){
56010                 row++;
56011                 first = false;
56012             }
56013             while(row < rlen){
56014                 if(!first){
56015                     col = 0;
56016                 }
56017                 first = false;
56018                 while(col < clen){
56019                     if(fn.call(scope || this, row, col, cm) === true){
56020                         return [row, col];
56021                     }
56022                     col++;
56023                 }
56024                 row++;
56025             }
56026         }
56027         return null;
56028     },
56029
56030     // private
56031     getSelections : function(){
56032         return this.selModel.getSelections();
56033     },
56034
56035     /**
56036      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56037      * but if manual update is required this method will initiate it.
56038      */
56039     autoSize : function(){
56040         if(this.rendered){
56041             this.view.layout();
56042             if(this.view.adjustForScroll){
56043                 this.view.adjustForScroll();
56044             }
56045         }
56046     },
56047
56048     /**
56049      * Returns the grid's underlying element.
56050      * @return {Element} The element
56051      */
56052     getGridEl : function(){
56053         return this.container;
56054     },
56055
56056     // private for compatibility, overridden by editor grid
56057     stopEditing : function(){},
56058
56059     /**
56060      * Returns the grid's SelectionModel.
56061      * @return {SelectionModel}
56062      */
56063     getSelectionModel : function(){
56064         if(!this.selModel){
56065             this.selModel = new Roo.grid.RowSelectionModel();
56066         }
56067         return this.selModel;
56068     },
56069
56070     /**
56071      * Returns the grid's DataSource.
56072      * @return {DataSource}
56073      */
56074     getDataSource : function(){
56075         return this.dataSource;
56076     },
56077
56078     /**
56079      * Returns the grid's ColumnModel.
56080      * @return {ColumnModel}
56081      */
56082     getColumnModel : function(){
56083         return this.colModel;
56084     },
56085
56086     /**
56087      * Returns the grid's GridView object.
56088      * @return {GridView}
56089      */
56090     getView : function(){
56091         if(!this.view){
56092             this.view = new Roo.grid.GridView(this.viewConfig);
56093             this.relayEvents(this.view, [
56094                 "beforerowremoved", "beforerowsinserted",
56095                 "beforerefresh", "rowremoved",
56096                 "rowsinserted", "rowupdated" ,"refresh"
56097             ]);
56098         }
56099         return this.view;
56100     },
56101     /**
56102      * Called to get grid's drag proxy text, by default returns this.ddText.
56103      * Override this to put something different in the dragged text.
56104      * @return {String}
56105      */
56106     getDragDropText : function(){
56107         var count = this.selModel.getCount();
56108         return String.format(this.ddText, count, count == 1 ? '' : 's');
56109     }
56110 });
56111 /*
56112  * Based on:
56113  * Ext JS Library 1.1.1
56114  * Copyright(c) 2006-2007, Ext JS, LLC.
56115  *
56116  * Originally Released Under LGPL - original licence link has changed is not relivant.
56117  *
56118  * Fork - LGPL
56119  * <script type="text/javascript">
56120  */
56121  /**
56122  * @class Roo.grid.AbstractGridView
56123  * @extends Roo.util.Observable
56124  * @abstract
56125  * Abstract base class for grid Views
56126  * @constructor
56127  */
56128 Roo.grid.AbstractGridView = function(){
56129         this.grid = null;
56130         
56131         this.events = {
56132             "beforerowremoved" : true,
56133             "beforerowsinserted" : true,
56134             "beforerefresh" : true,
56135             "rowremoved" : true,
56136             "rowsinserted" : true,
56137             "rowupdated" : true,
56138             "refresh" : true
56139         };
56140     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56141 };
56142
56143 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56144     rowClass : "x-grid-row",
56145     cellClass : "x-grid-cell",
56146     tdClass : "x-grid-td",
56147     hdClass : "x-grid-hd",
56148     splitClass : "x-grid-hd-split",
56149     
56150     init: function(grid){
56151         this.grid = grid;
56152                 var cid = this.grid.getGridEl().id;
56153         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56154         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56155         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56156         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56157         },
56158         
56159     getColumnRenderers : function(){
56160         var renderers = [];
56161         var cm = this.grid.colModel;
56162         var colCount = cm.getColumnCount();
56163         for(var i = 0; i < colCount; i++){
56164             renderers[i] = cm.getRenderer(i);
56165         }
56166         return renderers;
56167     },
56168     
56169     getColumnIds : function(){
56170         var ids = [];
56171         var cm = this.grid.colModel;
56172         var colCount = cm.getColumnCount();
56173         for(var i = 0; i < colCount; i++){
56174             ids[i] = cm.getColumnId(i);
56175         }
56176         return ids;
56177     },
56178     
56179     getDataIndexes : function(){
56180         if(!this.indexMap){
56181             this.indexMap = this.buildIndexMap();
56182         }
56183         return this.indexMap.colToData;
56184     },
56185     
56186     getColumnIndexByDataIndex : function(dataIndex){
56187         if(!this.indexMap){
56188             this.indexMap = this.buildIndexMap();
56189         }
56190         return this.indexMap.dataToCol[dataIndex];
56191     },
56192     
56193     /**
56194      * Set a css style for a column dynamically. 
56195      * @param {Number} colIndex The index of the column
56196      * @param {String} name The css property name
56197      * @param {String} value The css value
56198      */
56199     setCSSStyle : function(colIndex, name, value){
56200         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56201         Roo.util.CSS.updateRule(selector, name, value);
56202     },
56203     
56204     generateRules : function(cm){
56205         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56206         Roo.util.CSS.removeStyleSheet(rulesId);
56207         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56208             var cid = cm.getColumnId(i);
56209             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56210                          this.tdSelector, cid, " {\n}\n",
56211                          this.hdSelector, cid, " {\n}\n",
56212                          this.splitSelector, cid, " {\n}\n");
56213         }
56214         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56215     }
56216 });/*
56217  * Based on:
56218  * Ext JS Library 1.1.1
56219  * Copyright(c) 2006-2007, Ext JS, LLC.
56220  *
56221  * Originally Released Under LGPL - original licence link has changed is not relivant.
56222  *
56223  * Fork - LGPL
56224  * <script type="text/javascript">
56225  */
56226
56227 // private
56228 // This is a support class used internally by the Grid components
56229 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56230     this.grid = grid;
56231     this.view = grid.getView();
56232     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56233     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56234     if(hd2){
56235         this.setHandleElId(Roo.id(hd));
56236         this.setOuterHandleElId(Roo.id(hd2));
56237     }
56238     this.scroll = false;
56239 };
56240 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56241     maxDragWidth: 120,
56242     getDragData : function(e){
56243         var t = Roo.lib.Event.getTarget(e);
56244         var h = this.view.findHeaderCell(t);
56245         if(h){
56246             return {ddel: h.firstChild, header:h};
56247         }
56248         return false;
56249     },
56250
56251     onInitDrag : function(e){
56252         this.view.headersDisabled = true;
56253         var clone = this.dragData.ddel.cloneNode(true);
56254         clone.id = Roo.id();
56255         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56256         this.proxy.update(clone);
56257         return true;
56258     },
56259
56260     afterValidDrop : function(){
56261         var v = this.view;
56262         setTimeout(function(){
56263             v.headersDisabled = false;
56264         }, 50);
56265     },
56266
56267     afterInvalidDrop : function(){
56268         var v = this.view;
56269         setTimeout(function(){
56270             v.headersDisabled = false;
56271         }, 50);
56272     }
56273 });
56274 /*
56275  * Based on:
56276  * Ext JS Library 1.1.1
56277  * Copyright(c) 2006-2007, Ext JS, LLC.
56278  *
56279  * Originally Released Under LGPL - original licence link has changed is not relivant.
56280  *
56281  * Fork - LGPL
56282  * <script type="text/javascript">
56283  */
56284 // private
56285 // This is a support class used internally by the Grid components
56286 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56287     this.grid = grid;
56288     this.view = grid.getView();
56289     // split the proxies so they don't interfere with mouse events
56290     this.proxyTop = Roo.DomHelper.append(document.body, {
56291         cls:"col-move-top", html:"&#160;"
56292     }, true);
56293     this.proxyBottom = Roo.DomHelper.append(document.body, {
56294         cls:"col-move-bottom", html:"&#160;"
56295     }, true);
56296     this.proxyTop.hide = this.proxyBottom.hide = function(){
56297         this.setLeftTop(-100,-100);
56298         this.setStyle("visibility", "hidden");
56299     };
56300     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56301     // temporarily disabled
56302     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56303     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56304 };
56305 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56306     proxyOffsets : [-4, -9],
56307     fly: Roo.Element.fly,
56308
56309     getTargetFromEvent : function(e){
56310         var t = Roo.lib.Event.getTarget(e);
56311         var cindex = this.view.findCellIndex(t);
56312         if(cindex !== false){
56313             return this.view.getHeaderCell(cindex);
56314         }
56315         return null;
56316     },
56317
56318     nextVisible : function(h){
56319         var v = this.view, cm = this.grid.colModel;
56320         h = h.nextSibling;
56321         while(h){
56322             if(!cm.isHidden(v.getCellIndex(h))){
56323                 return h;
56324             }
56325             h = h.nextSibling;
56326         }
56327         return null;
56328     },
56329
56330     prevVisible : function(h){
56331         var v = this.view, cm = this.grid.colModel;
56332         h = h.prevSibling;
56333         while(h){
56334             if(!cm.isHidden(v.getCellIndex(h))){
56335                 return h;
56336             }
56337             h = h.prevSibling;
56338         }
56339         return null;
56340     },
56341
56342     positionIndicator : function(h, n, e){
56343         var x = Roo.lib.Event.getPageX(e);
56344         var r = Roo.lib.Dom.getRegion(n.firstChild);
56345         var px, pt, py = r.top + this.proxyOffsets[1];
56346         if((r.right - x) <= (r.right-r.left)/2){
56347             px = r.right+this.view.borderWidth;
56348             pt = "after";
56349         }else{
56350             px = r.left;
56351             pt = "before";
56352         }
56353         var oldIndex = this.view.getCellIndex(h);
56354         var newIndex = this.view.getCellIndex(n);
56355
56356         if(this.grid.colModel.isFixed(newIndex)){
56357             return false;
56358         }
56359
56360         var locked = this.grid.colModel.isLocked(newIndex);
56361
56362         if(pt == "after"){
56363             newIndex++;
56364         }
56365         if(oldIndex < newIndex){
56366             newIndex--;
56367         }
56368         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56369             return false;
56370         }
56371         px +=  this.proxyOffsets[0];
56372         this.proxyTop.setLeftTop(px, py);
56373         this.proxyTop.show();
56374         if(!this.bottomOffset){
56375             this.bottomOffset = this.view.mainHd.getHeight();
56376         }
56377         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56378         this.proxyBottom.show();
56379         return pt;
56380     },
56381
56382     onNodeEnter : function(n, dd, e, data){
56383         if(data.header != n){
56384             this.positionIndicator(data.header, n, e);
56385         }
56386     },
56387
56388     onNodeOver : function(n, dd, e, data){
56389         var result = false;
56390         if(data.header != n){
56391             result = this.positionIndicator(data.header, n, e);
56392         }
56393         if(!result){
56394             this.proxyTop.hide();
56395             this.proxyBottom.hide();
56396         }
56397         return result ? this.dropAllowed : this.dropNotAllowed;
56398     },
56399
56400     onNodeOut : function(n, dd, e, data){
56401         this.proxyTop.hide();
56402         this.proxyBottom.hide();
56403     },
56404
56405     onNodeDrop : function(n, dd, e, data){
56406         var h = data.header;
56407         if(h != n){
56408             var cm = this.grid.colModel;
56409             var x = Roo.lib.Event.getPageX(e);
56410             var r = Roo.lib.Dom.getRegion(n.firstChild);
56411             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56412             var oldIndex = this.view.getCellIndex(h);
56413             var newIndex = this.view.getCellIndex(n);
56414             var locked = cm.isLocked(newIndex);
56415             if(pt == "after"){
56416                 newIndex++;
56417             }
56418             if(oldIndex < newIndex){
56419                 newIndex--;
56420             }
56421             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56422                 return false;
56423             }
56424             cm.setLocked(oldIndex, locked, true);
56425             cm.moveColumn(oldIndex, newIndex);
56426             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56427             return true;
56428         }
56429         return false;
56430     }
56431 });
56432 /*
56433  * Based on:
56434  * Ext JS Library 1.1.1
56435  * Copyright(c) 2006-2007, Ext JS, LLC.
56436  *
56437  * Originally Released Under LGPL - original licence link has changed is not relivant.
56438  *
56439  * Fork - LGPL
56440  * <script type="text/javascript">
56441  */
56442   
56443 /**
56444  * @class Roo.grid.GridView
56445  * @extends Roo.util.Observable
56446  *
56447  * @constructor
56448  * @param {Object} config
56449  */
56450 Roo.grid.GridView = function(config){
56451     Roo.grid.GridView.superclass.constructor.call(this);
56452     this.el = null;
56453
56454     Roo.apply(this, config);
56455 };
56456
56457 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56458
56459     unselectable :  'unselectable="on"',
56460     unselectableCls :  'x-unselectable',
56461     
56462     
56463     rowClass : "x-grid-row",
56464
56465     cellClass : "x-grid-col",
56466
56467     tdClass : "x-grid-td",
56468
56469     hdClass : "x-grid-hd",
56470
56471     splitClass : "x-grid-split",
56472
56473     sortClasses : ["sort-asc", "sort-desc"],
56474
56475     enableMoveAnim : false,
56476
56477     hlColor: "C3DAF9",
56478
56479     dh : Roo.DomHelper,
56480
56481     fly : Roo.Element.fly,
56482
56483     css : Roo.util.CSS,
56484
56485     borderWidth: 1,
56486
56487     splitOffset: 3,
56488
56489     scrollIncrement : 22,
56490
56491     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56492
56493     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56494
56495     bind : function(ds, cm){
56496         if(this.ds){
56497             this.ds.un("load", this.onLoad, this);
56498             this.ds.un("datachanged", this.onDataChange, this);
56499             this.ds.un("add", this.onAdd, this);
56500             this.ds.un("remove", this.onRemove, this);
56501             this.ds.un("update", this.onUpdate, this);
56502             this.ds.un("clear", this.onClear, this);
56503         }
56504         if(ds){
56505             ds.on("load", this.onLoad, this);
56506             ds.on("datachanged", this.onDataChange, this);
56507             ds.on("add", this.onAdd, this);
56508             ds.on("remove", this.onRemove, this);
56509             ds.on("update", this.onUpdate, this);
56510             ds.on("clear", this.onClear, this);
56511         }
56512         this.ds = ds;
56513
56514         if(this.cm){
56515             this.cm.un("widthchange", this.onColWidthChange, this);
56516             this.cm.un("headerchange", this.onHeaderChange, this);
56517             this.cm.un("hiddenchange", this.onHiddenChange, this);
56518             this.cm.un("columnmoved", this.onColumnMove, this);
56519             this.cm.un("columnlockchange", this.onColumnLock, this);
56520         }
56521         if(cm){
56522             this.generateRules(cm);
56523             cm.on("widthchange", this.onColWidthChange, this);
56524             cm.on("headerchange", this.onHeaderChange, this);
56525             cm.on("hiddenchange", this.onHiddenChange, this);
56526             cm.on("columnmoved", this.onColumnMove, this);
56527             cm.on("columnlockchange", this.onColumnLock, this);
56528         }
56529         this.cm = cm;
56530     },
56531
56532     init: function(grid){
56533         Roo.grid.GridView.superclass.init.call(this, grid);
56534
56535         this.bind(grid.dataSource, grid.colModel);
56536
56537         grid.on("headerclick", this.handleHeaderClick, this);
56538
56539         if(grid.trackMouseOver){
56540             grid.on("mouseover", this.onRowOver, this);
56541             grid.on("mouseout", this.onRowOut, this);
56542         }
56543         grid.cancelTextSelection = function(){};
56544         this.gridId = grid.id;
56545
56546         var tpls = this.templates || {};
56547
56548         if(!tpls.master){
56549             tpls.master = new Roo.Template(
56550                '<div class="x-grid" hidefocus="true">',
56551                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56552                   '<div class="x-grid-topbar"></div>',
56553                   '<div class="x-grid-scroller"><div></div></div>',
56554                   '<div class="x-grid-locked">',
56555                       '<div class="x-grid-header">{lockedHeader}</div>',
56556                       '<div class="x-grid-body">{lockedBody}</div>',
56557                   "</div>",
56558                   '<div class="x-grid-viewport">',
56559                       '<div class="x-grid-header">{header}</div>',
56560                       '<div class="x-grid-body">{body}</div>',
56561                   "</div>",
56562                   '<div class="x-grid-bottombar"></div>',
56563                  
56564                   '<div class="x-grid-resize-proxy">&#160;</div>',
56565                "</div>"
56566             );
56567             tpls.master.disableformats = true;
56568         }
56569
56570         if(!tpls.header){
56571             tpls.header = new Roo.Template(
56572                '<table border="0" cellspacing="0" cellpadding="0">',
56573                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56574                "</table>{splits}"
56575             );
56576             tpls.header.disableformats = true;
56577         }
56578         tpls.header.compile();
56579
56580         if(!tpls.hcell){
56581             tpls.hcell = new Roo.Template(
56582                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56583                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56584                 "</div></td>"
56585              );
56586              tpls.hcell.disableFormats = true;
56587         }
56588         tpls.hcell.compile();
56589
56590         if(!tpls.hsplit){
56591             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56592                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56593             tpls.hsplit.disableFormats = true;
56594         }
56595         tpls.hsplit.compile();
56596
56597         if(!tpls.body){
56598             tpls.body = new Roo.Template(
56599                '<table border="0" cellspacing="0" cellpadding="0">',
56600                "<tbody>{rows}</tbody>",
56601                "</table>"
56602             );
56603             tpls.body.disableFormats = true;
56604         }
56605         tpls.body.compile();
56606
56607         if(!tpls.row){
56608             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56609             tpls.row.disableFormats = true;
56610         }
56611         tpls.row.compile();
56612
56613         if(!tpls.cell){
56614             tpls.cell = new Roo.Template(
56615                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56616                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56617                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56618                 "</td>"
56619             );
56620             tpls.cell.disableFormats = true;
56621         }
56622         tpls.cell.compile();
56623
56624         this.templates = tpls;
56625     },
56626
56627     // remap these for backwards compat
56628     onColWidthChange : function(){
56629         this.updateColumns.apply(this, arguments);
56630     },
56631     onHeaderChange : function(){
56632         this.updateHeaders.apply(this, arguments);
56633     }, 
56634     onHiddenChange : function(){
56635         this.handleHiddenChange.apply(this, arguments);
56636     },
56637     onColumnMove : function(){
56638         this.handleColumnMove.apply(this, arguments);
56639     },
56640     onColumnLock : function(){
56641         this.handleLockChange.apply(this, arguments);
56642     },
56643
56644     onDataChange : function(){
56645         this.refresh();
56646         this.updateHeaderSortState();
56647     },
56648
56649     onClear : function(){
56650         this.refresh();
56651     },
56652
56653     onUpdate : function(ds, record){
56654         this.refreshRow(record);
56655     },
56656
56657     refreshRow : function(record){
56658         var ds = this.ds, index;
56659         if(typeof record == 'number'){
56660             index = record;
56661             record = ds.getAt(index);
56662         }else{
56663             index = ds.indexOf(record);
56664         }
56665         this.insertRows(ds, index, index, true);
56666         this.onRemove(ds, record, index+1, true);
56667         this.syncRowHeights(index, index);
56668         this.layout();
56669         this.fireEvent("rowupdated", this, index, record);
56670     },
56671
56672     onAdd : function(ds, records, index){
56673         this.insertRows(ds, index, index + (records.length-1));
56674     },
56675
56676     onRemove : function(ds, record, index, isUpdate){
56677         if(isUpdate !== true){
56678             this.fireEvent("beforerowremoved", this, index, record);
56679         }
56680         var bt = this.getBodyTable(), lt = this.getLockedTable();
56681         if(bt.rows[index]){
56682             bt.firstChild.removeChild(bt.rows[index]);
56683         }
56684         if(lt.rows[index]){
56685             lt.firstChild.removeChild(lt.rows[index]);
56686         }
56687         if(isUpdate !== true){
56688             this.stripeRows(index);
56689             this.syncRowHeights(index, index);
56690             this.layout();
56691             this.fireEvent("rowremoved", this, index, record);
56692         }
56693     },
56694
56695     onLoad : function(){
56696         this.scrollToTop();
56697     },
56698
56699     /**
56700      * Scrolls the grid to the top
56701      */
56702     scrollToTop : function(){
56703         if(this.scroller){
56704             this.scroller.dom.scrollTop = 0;
56705             this.syncScroll();
56706         }
56707     },
56708
56709     /**
56710      * Gets a panel in the header of the grid that can be used for toolbars etc.
56711      * After modifying the contents of this panel a call to grid.autoSize() may be
56712      * required to register any changes in size.
56713      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56714      * @return Roo.Element
56715      */
56716     getHeaderPanel : function(doShow){
56717         if(doShow){
56718             this.headerPanel.show();
56719         }
56720         return this.headerPanel;
56721     },
56722
56723     /**
56724      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56725      * After modifying the contents of this panel a call to grid.autoSize() may be
56726      * required to register any changes in size.
56727      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56728      * @return Roo.Element
56729      */
56730     getFooterPanel : function(doShow){
56731         if(doShow){
56732             this.footerPanel.show();
56733         }
56734         return this.footerPanel;
56735     },
56736
56737     initElements : function(){
56738         var E = Roo.Element;
56739         var el = this.grid.getGridEl().dom.firstChild;
56740         var cs = el.childNodes;
56741
56742         this.el = new E(el);
56743         
56744          this.focusEl = new E(el.firstChild);
56745         this.focusEl.swallowEvent("click", true);
56746         
56747         this.headerPanel = new E(cs[1]);
56748         this.headerPanel.enableDisplayMode("block");
56749
56750         this.scroller = new E(cs[2]);
56751         this.scrollSizer = new E(this.scroller.dom.firstChild);
56752
56753         this.lockedWrap = new E(cs[3]);
56754         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56755         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56756
56757         this.mainWrap = new E(cs[4]);
56758         this.mainHd = new E(this.mainWrap.dom.firstChild);
56759         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56760
56761         this.footerPanel = new E(cs[5]);
56762         this.footerPanel.enableDisplayMode("block");
56763
56764         this.resizeProxy = new E(cs[6]);
56765
56766         this.headerSelector = String.format(
56767            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56768            this.lockedHd.id, this.mainHd.id
56769         );
56770
56771         this.splitterSelector = String.format(
56772            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56773            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56774         );
56775     },
56776     idToCssName : function(s)
56777     {
56778         return s.replace(/[^a-z0-9]+/ig, '-');
56779     },
56780
56781     getHeaderCell : function(index){
56782         return Roo.DomQuery.select(this.headerSelector)[index];
56783     },
56784
56785     getHeaderCellMeasure : function(index){
56786         return this.getHeaderCell(index).firstChild;
56787     },
56788
56789     getHeaderCellText : function(index){
56790         return this.getHeaderCell(index).firstChild.firstChild;
56791     },
56792
56793     getLockedTable : function(){
56794         return this.lockedBody.dom.firstChild;
56795     },
56796
56797     getBodyTable : function(){
56798         return this.mainBody.dom.firstChild;
56799     },
56800
56801     getLockedRow : function(index){
56802         return this.getLockedTable().rows[index];
56803     },
56804
56805     getRow : function(index){
56806         return this.getBodyTable().rows[index];
56807     },
56808
56809     getRowComposite : function(index){
56810         if(!this.rowEl){
56811             this.rowEl = new Roo.CompositeElementLite();
56812         }
56813         var els = [], lrow, mrow;
56814         if(lrow = this.getLockedRow(index)){
56815             els.push(lrow);
56816         }
56817         if(mrow = this.getRow(index)){
56818             els.push(mrow);
56819         }
56820         this.rowEl.elements = els;
56821         return this.rowEl;
56822     },
56823     /**
56824      * Gets the 'td' of the cell
56825      * 
56826      * @param {Integer} rowIndex row to select
56827      * @param {Integer} colIndex column to select
56828      * 
56829      * @return {Object} 
56830      */
56831     getCell : function(rowIndex, colIndex){
56832         var locked = this.cm.getLockedCount();
56833         var source;
56834         if(colIndex < locked){
56835             source = this.lockedBody.dom.firstChild;
56836         }else{
56837             source = this.mainBody.dom.firstChild;
56838             colIndex -= locked;
56839         }
56840         return source.rows[rowIndex].childNodes[colIndex];
56841     },
56842
56843     getCellText : function(rowIndex, colIndex){
56844         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56845     },
56846
56847     getCellBox : function(cell){
56848         var b = this.fly(cell).getBox();
56849         if(Roo.isOpera){ // opera fails to report the Y
56850             b.y = cell.offsetTop + this.mainBody.getY();
56851         }
56852         return b;
56853     },
56854
56855     getCellIndex : function(cell){
56856         var id = String(cell.className).match(this.cellRE);
56857         if(id){
56858             return parseInt(id[1], 10);
56859         }
56860         return 0;
56861     },
56862
56863     findHeaderIndex : function(n){
56864         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56865         return r ? this.getCellIndex(r) : false;
56866     },
56867
56868     findHeaderCell : function(n){
56869         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56870         return r ? r : false;
56871     },
56872
56873     findRowIndex : function(n){
56874         if(!n){
56875             return false;
56876         }
56877         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56878         return r ? r.rowIndex : false;
56879     },
56880
56881     findCellIndex : function(node){
56882         var stop = this.el.dom;
56883         while(node && node != stop){
56884             if(this.findRE.test(node.className)){
56885                 return this.getCellIndex(node);
56886             }
56887             node = node.parentNode;
56888         }
56889         return false;
56890     },
56891
56892     getColumnId : function(index){
56893         return this.cm.getColumnId(index);
56894     },
56895
56896     getSplitters : function()
56897     {
56898         if(this.splitterSelector){
56899            return Roo.DomQuery.select(this.splitterSelector);
56900         }else{
56901             return null;
56902       }
56903     },
56904
56905     getSplitter : function(index){
56906         return this.getSplitters()[index];
56907     },
56908
56909     onRowOver : function(e, t){
56910         var row;
56911         if((row = this.findRowIndex(t)) !== false){
56912             this.getRowComposite(row).addClass("x-grid-row-over");
56913         }
56914     },
56915
56916     onRowOut : function(e, t){
56917         var row;
56918         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56919             this.getRowComposite(row).removeClass("x-grid-row-over");
56920         }
56921     },
56922
56923     renderHeaders : function(){
56924         var cm = this.cm;
56925         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56926         var cb = [], lb = [], sb = [], lsb = [], p = {};
56927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56928             p.cellId = "x-grid-hd-0-" + i;
56929             p.splitId = "x-grid-csplit-0-" + i;
56930             p.id = cm.getColumnId(i);
56931             p.value = cm.getColumnHeader(i) || "";
56932             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56933             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56934             if(!cm.isLocked(i)){
56935                 cb[cb.length] = ct.apply(p);
56936                 sb[sb.length] = st.apply(p);
56937             }else{
56938                 lb[lb.length] = ct.apply(p);
56939                 lsb[lsb.length] = st.apply(p);
56940             }
56941         }
56942         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56943                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56944     },
56945
56946     updateHeaders : function(){
56947         var html = this.renderHeaders();
56948         this.lockedHd.update(html[0]);
56949         this.mainHd.update(html[1]);
56950     },
56951
56952     /**
56953      * Focuses the specified row.
56954      * @param {Number} row The row index
56955      */
56956     focusRow : function(row)
56957     {
56958         //Roo.log('GridView.focusRow');
56959         var x = this.scroller.dom.scrollLeft;
56960         this.focusCell(row, 0, false);
56961         this.scroller.dom.scrollLeft = x;
56962     },
56963
56964     /**
56965      * Focuses the specified cell.
56966      * @param {Number} row The row index
56967      * @param {Number} col The column index
56968      * @param {Boolean} hscroll false to disable horizontal scrolling
56969      */
56970     focusCell : function(row, col, hscroll)
56971     {
56972         //Roo.log('GridView.focusCell');
56973         var el = this.ensureVisible(row, col, hscroll);
56974         this.focusEl.alignTo(el, "tl-tl");
56975         if(Roo.isGecko){
56976             this.focusEl.focus();
56977         }else{
56978             this.focusEl.focus.defer(1, this.focusEl);
56979         }
56980     },
56981
56982     /**
56983      * Scrolls the specified cell into view
56984      * @param {Number} row The row index
56985      * @param {Number} col The column index
56986      * @param {Boolean} hscroll false to disable horizontal scrolling
56987      */
56988     ensureVisible : function(row, col, hscroll)
56989     {
56990         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56991         //return null; //disable for testing.
56992         if(typeof row != "number"){
56993             row = row.rowIndex;
56994         }
56995         if(row < 0 && row >= this.ds.getCount()){
56996             return  null;
56997         }
56998         col = (col !== undefined ? col : 0);
56999         var cm = this.grid.colModel;
57000         while(cm.isHidden(col)){
57001             col++;
57002         }
57003
57004         var el = this.getCell(row, col);
57005         if(!el){
57006             return null;
57007         }
57008         var c = this.scroller.dom;
57009
57010         var ctop = parseInt(el.offsetTop, 10);
57011         var cleft = parseInt(el.offsetLeft, 10);
57012         var cbot = ctop + el.offsetHeight;
57013         var cright = cleft + el.offsetWidth;
57014         
57015         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57016         var stop = parseInt(c.scrollTop, 10);
57017         var sleft = parseInt(c.scrollLeft, 10);
57018         var sbot = stop + ch;
57019         var sright = sleft + c.clientWidth;
57020         /*
57021         Roo.log('GridView.ensureVisible:' +
57022                 ' ctop:' + ctop +
57023                 ' c.clientHeight:' + c.clientHeight +
57024                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57025                 ' stop:' + stop +
57026                 ' cbot:' + cbot +
57027                 ' sbot:' + sbot +
57028                 ' ch:' + ch  
57029                 );
57030         */
57031         if(ctop < stop){
57032             c.scrollTop = ctop;
57033             //Roo.log("set scrolltop to ctop DISABLE?");
57034         }else if(cbot > sbot){
57035             //Roo.log("set scrolltop to cbot-ch");
57036             c.scrollTop = cbot-ch;
57037         }
57038         
57039         if(hscroll !== false){
57040             if(cleft < sleft){
57041                 c.scrollLeft = cleft;
57042             }else if(cright > sright){
57043                 c.scrollLeft = cright-c.clientWidth;
57044             }
57045         }
57046          
57047         return el;
57048     },
57049
57050     updateColumns : function(){
57051         this.grid.stopEditing();
57052         var cm = this.grid.colModel, colIds = this.getColumnIds();
57053         //var totalWidth = cm.getTotalWidth();
57054         var pos = 0;
57055         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57056             //if(cm.isHidden(i)) continue;
57057             var w = cm.getColumnWidth(i);
57058             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57059             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57060         }
57061         this.updateSplitters();
57062     },
57063
57064     generateRules : function(cm){
57065         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57066         Roo.util.CSS.removeStyleSheet(rulesId);
57067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57068             var cid = cm.getColumnId(i);
57069             var align = '';
57070             if(cm.config[i].align){
57071                 align = 'text-align:'+cm.config[i].align+';';
57072             }
57073             var hidden = '';
57074             if(cm.isHidden(i)){
57075                 hidden = 'display:none;';
57076             }
57077             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57078             ruleBuf.push(
57079                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57080                     this.hdSelector, cid, " {\n", align, width, "}\n",
57081                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57082                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57083         }
57084         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57085     },
57086
57087     updateSplitters : function(){
57088         var cm = this.cm, s = this.getSplitters();
57089         if(s){ // splitters not created yet
57090             var pos = 0, locked = true;
57091             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57092                 if(cm.isHidden(i)) {
57093                     continue;
57094                 }
57095                 var w = cm.getColumnWidth(i); // make sure it's a number
57096                 if(!cm.isLocked(i) && locked){
57097                     pos = 0;
57098                     locked = false;
57099                 }
57100                 pos += w;
57101                 s[i].style.left = (pos-this.splitOffset) + "px";
57102             }
57103         }
57104     },
57105
57106     handleHiddenChange : function(colModel, colIndex, hidden){
57107         if(hidden){
57108             this.hideColumn(colIndex);
57109         }else{
57110             this.unhideColumn(colIndex);
57111         }
57112     },
57113
57114     hideColumn : function(colIndex){
57115         var cid = this.getColumnId(colIndex);
57116         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57117         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57118         if(Roo.isSafari){
57119             this.updateHeaders();
57120         }
57121         this.updateSplitters();
57122         this.layout();
57123     },
57124
57125     unhideColumn : function(colIndex){
57126         var cid = this.getColumnId(colIndex);
57127         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57128         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57129
57130         if(Roo.isSafari){
57131             this.updateHeaders();
57132         }
57133         this.updateSplitters();
57134         this.layout();
57135     },
57136
57137     insertRows : function(dm, firstRow, lastRow, isUpdate){
57138         if(firstRow == 0 && lastRow == dm.getCount()-1){
57139             this.refresh();
57140         }else{
57141             if(!isUpdate){
57142                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57143             }
57144             var s = this.getScrollState();
57145             var markup = this.renderRows(firstRow, lastRow);
57146             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57147             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57148             this.restoreScroll(s);
57149             if(!isUpdate){
57150                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57151                 this.syncRowHeights(firstRow, lastRow);
57152                 this.stripeRows(firstRow);
57153                 this.layout();
57154             }
57155         }
57156     },
57157
57158     bufferRows : function(markup, target, index){
57159         var before = null, trows = target.rows, tbody = target.tBodies[0];
57160         if(index < trows.length){
57161             before = trows[index];
57162         }
57163         var b = document.createElement("div");
57164         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57165         var rows = b.firstChild.rows;
57166         for(var i = 0, len = rows.length; i < len; i++){
57167             if(before){
57168                 tbody.insertBefore(rows[0], before);
57169             }else{
57170                 tbody.appendChild(rows[0]);
57171             }
57172         }
57173         b.innerHTML = "";
57174         b = null;
57175     },
57176
57177     deleteRows : function(dm, firstRow, lastRow){
57178         if(dm.getRowCount()<1){
57179             this.fireEvent("beforerefresh", this);
57180             this.mainBody.update("");
57181             this.lockedBody.update("");
57182             this.fireEvent("refresh", this);
57183         }else{
57184             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57185             var bt = this.getBodyTable();
57186             var tbody = bt.firstChild;
57187             var rows = bt.rows;
57188             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57189                 tbody.removeChild(rows[firstRow]);
57190             }
57191             this.stripeRows(firstRow);
57192             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57193         }
57194     },
57195
57196     updateRows : function(dataSource, firstRow, lastRow){
57197         var s = this.getScrollState();
57198         this.refresh();
57199         this.restoreScroll(s);
57200     },
57201
57202     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57203         if(!noRefresh){
57204            this.refresh();
57205         }
57206         this.updateHeaderSortState();
57207     },
57208
57209     getScrollState : function(){
57210         
57211         var sb = this.scroller.dom;
57212         return {left: sb.scrollLeft, top: sb.scrollTop};
57213     },
57214
57215     stripeRows : function(startRow){
57216         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57217             return;
57218         }
57219         startRow = startRow || 0;
57220         var rows = this.getBodyTable().rows;
57221         var lrows = this.getLockedTable().rows;
57222         var cls = ' x-grid-row-alt ';
57223         for(var i = startRow, len = rows.length; i < len; i++){
57224             var row = rows[i], lrow = lrows[i];
57225             var isAlt = ((i+1) % 2 == 0);
57226             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57227             if(isAlt == hasAlt){
57228                 continue;
57229             }
57230             if(isAlt){
57231                 row.className += " x-grid-row-alt";
57232             }else{
57233                 row.className = row.className.replace("x-grid-row-alt", "");
57234             }
57235             if(lrow){
57236                 lrow.className = row.className;
57237             }
57238         }
57239     },
57240
57241     restoreScroll : function(state){
57242         //Roo.log('GridView.restoreScroll');
57243         var sb = this.scroller.dom;
57244         sb.scrollLeft = state.left;
57245         sb.scrollTop = state.top;
57246         this.syncScroll();
57247     },
57248
57249     syncScroll : function(){
57250         //Roo.log('GridView.syncScroll');
57251         var sb = this.scroller.dom;
57252         var sh = this.mainHd.dom;
57253         var bs = this.mainBody.dom;
57254         var lv = this.lockedBody.dom;
57255         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57256         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57257     },
57258
57259     handleScroll : function(e){
57260         this.syncScroll();
57261         var sb = this.scroller.dom;
57262         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57263         e.stopEvent();
57264     },
57265
57266     handleWheel : function(e){
57267         var d = e.getWheelDelta();
57268         this.scroller.dom.scrollTop -= d*22;
57269         // set this here to prevent jumpy scrolling on large tables
57270         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57271         e.stopEvent();
57272     },
57273
57274     renderRows : function(startRow, endRow){
57275         // pull in all the crap needed to render rows
57276         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57277         var colCount = cm.getColumnCount();
57278
57279         if(ds.getCount() < 1){
57280             return ["", ""];
57281         }
57282
57283         // build a map for all the columns
57284         var cs = [];
57285         for(var i = 0; i < colCount; i++){
57286             var name = cm.getDataIndex(i);
57287             cs[i] = {
57288                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57289                 renderer : cm.getRenderer(i),
57290                 id : cm.getColumnId(i),
57291                 locked : cm.isLocked(i),
57292                 has_editor : cm.isCellEditable(i)
57293             };
57294         }
57295
57296         startRow = startRow || 0;
57297         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57298
57299         // records to render
57300         var rs = ds.getRange(startRow, endRow);
57301
57302         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57303     },
57304
57305     // As much as I hate to duplicate code, this was branched because FireFox really hates
57306     // [].join("") on strings. The performance difference was substantial enough to
57307     // branch this function
57308     doRender : Roo.isGecko ?
57309             function(cs, rs, ds, startRow, colCount, stripe){
57310                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57311                 // buffers
57312                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57313                 
57314                 var hasListener = this.grid.hasListener('rowclass');
57315                 var rowcfg = {};
57316                 for(var j = 0, len = rs.length; j < len; j++){
57317                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57318                     for(var i = 0; i < colCount; i++){
57319                         c = cs[i];
57320                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57321                         p.id = c.id;
57322                         p.css = p.attr = "";
57323                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57324                         if(p.value == undefined || p.value === "") {
57325                             p.value = "&#160;";
57326                         }
57327                         if(c.has_editor){
57328                             p.css += ' x-grid-editable-cell';
57329                         }
57330                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57331                             p.css +=  ' x-grid-dirty-cell';
57332                         }
57333                         var markup = ct.apply(p);
57334                         if(!c.locked){
57335                             cb+= markup;
57336                         }else{
57337                             lcb+= markup;
57338                         }
57339                     }
57340                     var alt = [];
57341                     if(stripe && ((rowIndex+1) % 2 == 0)){
57342                         alt.push("x-grid-row-alt")
57343                     }
57344                     if(r.dirty){
57345                         alt.push(  " x-grid-dirty-row");
57346                     }
57347                     rp.cells = lcb;
57348                     if(this.getRowClass){
57349                         alt.push(this.getRowClass(r, rowIndex));
57350                     }
57351                     if (hasListener) {
57352                         rowcfg = {
57353                              
57354                             record: r,
57355                             rowIndex : rowIndex,
57356                             rowClass : ''
57357                         };
57358                         this.grid.fireEvent('rowclass', this, rowcfg);
57359                         alt.push(rowcfg.rowClass);
57360                     }
57361                     rp.alt = alt.join(" ");
57362                     lbuf+= rt.apply(rp);
57363                     rp.cells = cb;
57364                     buf+=  rt.apply(rp);
57365                 }
57366                 return [lbuf, buf];
57367             } :
57368             function(cs, rs, ds, startRow, colCount, stripe){
57369                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57370                 // buffers
57371                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57372                 var hasListener = this.grid.hasListener('rowclass');
57373  
57374                 var rowcfg = {};
57375                 for(var j = 0, len = rs.length; j < len; j++){
57376                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57377                     for(var i = 0; i < colCount; i++){
57378                         c = cs[i];
57379                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57380                         p.id = c.id;
57381                         p.css = p.attr = "";
57382                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57383                         if(p.value == undefined || p.value === "") {
57384                             p.value = "&#160;";
57385                         }
57386                         //Roo.log(c);
57387                          if(c.has_editor){
57388                             p.css += ' x-grid-editable-cell';
57389                         }
57390                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57391                             p.css += ' x-grid-dirty-cell' 
57392                         }
57393                         
57394                         var markup = ct.apply(p);
57395                         if(!c.locked){
57396                             cb[cb.length] = markup;
57397                         }else{
57398                             lcb[lcb.length] = markup;
57399                         }
57400                     }
57401                     var alt = [];
57402                     if(stripe && ((rowIndex+1) % 2 == 0)){
57403                         alt.push( "x-grid-row-alt");
57404                     }
57405                     if(r.dirty){
57406                         alt.push(" x-grid-dirty-row");
57407                     }
57408                     rp.cells = lcb;
57409                     if(this.getRowClass){
57410                         alt.push( this.getRowClass(r, rowIndex));
57411                     }
57412                     if (hasListener) {
57413                         rowcfg = {
57414                              
57415                             record: r,
57416                             rowIndex : rowIndex,
57417                             rowClass : ''
57418                         };
57419                         this.grid.fireEvent('rowclass', this, rowcfg);
57420                         alt.push(rowcfg.rowClass);
57421                     }
57422                     
57423                     rp.alt = alt.join(" ");
57424                     rp.cells = lcb.join("");
57425                     lbuf[lbuf.length] = rt.apply(rp);
57426                     rp.cells = cb.join("");
57427                     buf[buf.length] =  rt.apply(rp);
57428                 }
57429                 return [lbuf.join(""), buf.join("")];
57430             },
57431
57432     renderBody : function(){
57433         var markup = this.renderRows();
57434         var bt = this.templates.body;
57435         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57436     },
57437
57438     /**
57439      * Refreshes the grid
57440      * @param {Boolean} headersToo
57441      */
57442     refresh : function(headersToo){
57443         this.fireEvent("beforerefresh", this);
57444         this.grid.stopEditing();
57445         var result = this.renderBody();
57446         this.lockedBody.update(result[0]);
57447         this.mainBody.update(result[1]);
57448         if(headersToo === true){
57449             this.updateHeaders();
57450             this.updateColumns();
57451             this.updateSplitters();
57452             this.updateHeaderSortState();
57453         }
57454         this.syncRowHeights();
57455         this.layout();
57456         this.fireEvent("refresh", this);
57457     },
57458
57459     handleColumnMove : function(cm, oldIndex, newIndex){
57460         this.indexMap = null;
57461         var s = this.getScrollState();
57462         this.refresh(true);
57463         this.restoreScroll(s);
57464         this.afterMove(newIndex);
57465     },
57466
57467     afterMove : function(colIndex){
57468         if(this.enableMoveAnim && Roo.enableFx){
57469             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57470         }
57471         // if multisort - fix sortOrder, and reload..
57472         if (this.grid.dataSource.multiSort) {
57473             // the we can call sort again..
57474             var dm = this.grid.dataSource;
57475             var cm = this.grid.colModel;
57476             var so = [];
57477             for(var i = 0; i < cm.config.length; i++ ) {
57478                 
57479                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57480                     continue; // dont' bother, it's not in sort list or being set.
57481                 }
57482                 
57483                 so.push(cm.config[i].dataIndex);
57484             };
57485             dm.sortOrder = so;
57486             dm.load(dm.lastOptions);
57487             
57488             
57489         }
57490         
57491     },
57492
57493     updateCell : function(dm, rowIndex, dataIndex){
57494         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57495         if(typeof colIndex == "undefined"){ // not present in grid
57496             return;
57497         }
57498         var cm = this.grid.colModel;
57499         var cell = this.getCell(rowIndex, colIndex);
57500         var cellText = this.getCellText(rowIndex, colIndex);
57501
57502         var p = {
57503             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57504             id : cm.getColumnId(colIndex),
57505             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57506         };
57507         var renderer = cm.getRenderer(colIndex);
57508         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57509         if(typeof val == "undefined" || val === "") {
57510             val = "&#160;";
57511         }
57512         cellText.innerHTML = val;
57513         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57514         this.syncRowHeights(rowIndex, rowIndex);
57515     },
57516
57517     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57518         var maxWidth = 0;
57519         if(this.grid.autoSizeHeaders){
57520             var h = this.getHeaderCellMeasure(colIndex);
57521             maxWidth = Math.max(maxWidth, h.scrollWidth);
57522         }
57523         var tb, index;
57524         if(this.cm.isLocked(colIndex)){
57525             tb = this.getLockedTable();
57526             index = colIndex;
57527         }else{
57528             tb = this.getBodyTable();
57529             index = colIndex - this.cm.getLockedCount();
57530         }
57531         if(tb && tb.rows){
57532             var rows = tb.rows;
57533             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57534             for(var i = 0; i < stopIndex; i++){
57535                 var cell = rows[i].childNodes[index].firstChild;
57536                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57537             }
57538         }
57539         return maxWidth + /*margin for error in IE*/ 5;
57540     },
57541     /**
57542      * Autofit a column to its content.
57543      * @param {Number} colIndex
57544      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57545      */
57546      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57547          if(this.cm.isHidden(colIndex)){
57548              return; // can't calc a hidden column
57549          }
57550         if(forceMinSize){
57551             var cid = this.cm.getColumnId(colIndex);
57552             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57553            if(this.grid.autoSizeHeaders){
57554                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57555            }
57556         }
57557         var newWidth = this.calcColumnWidth(colIndex);
57558         this.cm.setColumnWidth(colIndex,
57559             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57560         if(!suppressEvent){
57561             this.grid.fireEvent("columnresize", colIndex, newWidth);
57562         }
57563     },
57564
57565     /**
57566      * Autofits all columns to their content and then expands to fit any extra space in the grid
57567      */
57568      autoSizeColumns : function(){
57569         var cm = this.grid.colModel;
57570         var colCount = cm.getColumnCount();
57571         for(var i = 0; i < colCount; i++){
57572             this.autoSizeColumn(i, true, true);
57573         }
57574         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57575             this.fitColumns();
57576         }else{
57577             this.updateColumns();
57578             this.layout();
57579         }
57580     },
57581
57582     /**
57583      * Autofits all columns to the grid's width proportionate with their current size
57584      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57585      */
57586     fitColumns : function(reserveScrollSpace){
57587         var cm = this.grid.colModel;
57588         var colCount = cm.getColumnCount();
57589         var cols = [];
57590         var width = 0;
57591         var i, w;
57592         for (i = 0; i < colCount; i++){
57593             if(!cm.isHidden(i) && !cm.isFixed(i)){
57594                 w = cm.getColumnWidth(i);
57595                 cols.push(i);
57596                 cols.push(w);
57597                 width += w;
57598             }
57599         }
57600         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57601         if(reserveScrollSpace){
57602             avail -= 17;
57603         }
57604         var frac = (avail - cm.getTotalWidth())/width;
57605         while (cols.length){
57606             w = cols.pop();
57607             i = cols.pop();
57608             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57609         }
57610         this.updateColumns();
57611         this.layout();
57612     },
57613
57614     onRowSelect : function(rowIndex){
57615         var row = this.getRowComposite(rowIndex);
57616         row.addClass("x-grid-row-selected");
57617     },
57618
57619     onRowDeselect : function(rowIndex){
57620         var row = this.getRowComposite(rowIndex);
57621         row.removeClass("x-grid-row-selected");
57622     },
57623
57624     onCellSelect : function(row, col){
57625         var cell = this.getCell(row, col);
57626         if(cell){
57627             Roo.fly(cell).addClass("x-grid-cell-selected");
57628         }
57629     },
57630
57631     onCellDeselect : function(row, col){
57632         var cell = this.getCell(row, col);
57633         if(cell){
57634             Roo.fly(cell).removeClass("x-grid-cell-selected");
57635         }
57636     },
57637
57638     updateHeaderSortState : function(){
57639         
57640         // sort state can be single { field: xxx, direction : yyy}
57641         // or   { xxx=>ASC , yyy : DESC ..... }
57642         
57643         var mstate = {};
57644         if (!this.ds.multiSort) { 
57645             var state = this.ds.getSortState();
57646             if(!state){
57647                 return;
57648             }
57649             mstate[state.field] = state.direction;
57650             // FIXME... - this is not used here.. but might be elsewhere..
57651             this.sortState = state;
57652             
57653         } else {
57654             mstate = this.ds.sortToggle;
57655         }
57656         //remove existing sort classes..
57657         
57658         var sc = this.sortClasses;
57659         var hds = this.el.select(this.headerSelector).removeClass(sc);
57660         
57661         for(var f in mstate) {
57662         
57663             var sortColumn = this.cm.findColumnIndex(f);
57664             
57665             if(sortColumn != -1){
57666                 var sortDir = mstate[f];        
57667                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57668             }
57669         }
57670         
57671          
57672         
57673     },
57674
57675
57676     handleHeaderClick : function(g, index,e){
57677         
57678         Roo.log("header click");
57679         
57680         if (Roo.isTouch) {
57681             // touch events on header are handled by context
57682             this.handleHdCtx(g,index,e);
57683             return;
57684         }
57685         
57686         
57687         if(this.headersDisabled){
57688             return;
57689         }
57690         var dm = g.dataSource, cm = g.colModel;
57691         if(!cm.isSortable(index)){
57692             return;
57693         }
57694         g.stopEditing();
57695         
57696         if (dm.multiSort) {
57697             // update the sortOrder
57698             var so = [];
57699             for(var i = 0; i < cm.config.length; i++ ) {
57700                 
57701                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57702                     continue; // dont' bother, it's not in sort list or being set.
57703                 }
57704                 
57705                 so.push(cm.config[i].dataIndex);
57706             };
57707             dm.sortOrder = so;
57708         }
57709         
57710         
57711         dm.sort(cm.getDataIndex(index));
57712     },
57713
57714
57715     destroy : function(){
57716         if(this.colMenu){
57717             this.colMenu.removeAll();
57718             Roo.menu.MenuMgr.unregister(this.colMenu);
57719             this.colMenu.getEl().remove();
57720             delete this.colMenu;
57721         }
57722         if(this.hmenu){
57723             this.hmenu.removeAll();
57724             Roo.menu.MenuMgr.unregister(this.hmenu);
57725             this.hmenu.getEl().remove();
57726             delete this.hmenu;
57727         }
57728         if(this.grid.enableColumnMove){
57729             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57730             if(dds){
57731                 for(var dd in dds){
57732                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57733                         var elid = dds[dd].dragElId;
57734                         dds[dd].unreg();
57735                         Roo.get(elid).remove();
57736                     } else if(dds[dd].config.isTarget){
57737                         dds[dd].proxyTop.remove();
57738                         dds[dd].proxyBottom.remove();
57739                         dds[dd].unreg();
57740                     }
57741                     if(Roo.dd.DDM.locationCache[dd]){
57742                         delete Roo.dd.DDM.locationCache[dd];
57743                     }
57744                 }
57745                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57746             }
57747         }
57748         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57749         this.bind(null, null);
57750         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57751     },
57752
57753     handleLockChange : function(){
57754         this.refresh(true);
57755     },
57756
57757     onDenyColumnLock : function(){
57758
57759     },
57760
57761     onDenyColumnHide : function(){
57762
57763     },
57764
57765     handleHdMenuClick : function(item){
57766         var index = this.hdCtxIndex;
57767         var cm = this.cm, ds = this.ds;
57768         switch(item.id){
57769             case "asc":
57770                 ds.sort(cm.getDataIndex(index), "ASC");
57771                 break;
57772             case "desc":
57773                 ds.sort(cm.getDataIndex(index), "DESC");
57774                 break;
57775             case "lock":
57776                 var lc = cm.getLockedCount();
57777                 if(cm.getColumnCount(true) <= lc+1){
57778                     this.onDenyColumnLock();
57779                     return;
57780                 }
57781                 if(lc != index){
57782                     cm.setLocked(index, true, true);
57783                     cm.moveColumn(index, lc);
57784                     this.grid.fireEvent("columnmove", index, lc);
57785                 }else{
57786                     cm.setLocked(index, true);
57787                 }
57788             break;
57789             case "unlock":
57790                 var lc = cm.getLockedCount();
57791                 if((lc-1) != index){
57792                     cm.setLocked(index, false, true);
57793                     cm.moveColumn(index, lc-1);
57794                     this.grid.fireEvent("columnmove", index, lc-1);
57795                 }else{
57796                     cm.setLocked(index, false);
57797                 }
57798             break;
57799             case 'wider': // used to expand cols on touch..
57800             case 'narrow':
57801                 var cw = cm.getColumnWidth(index);
57802                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57803                 cw = Math.max(0, cw);
57804                 cw = Math.min(cw,4000);
57805                 cm.setColumnWidth(index, cw);
57806                 break;
57807                 
57808             default:
57809                 index = cm.getIndexById(item.id.substr(4));
57810                 if(index != -1){
57811                     if(item.checked && cm.getColumnCount(true) <= 1){
57812                         this.onDenyColumnHide();
57813                         return false;
57814                     }
57815                     cm.setHidden(index, item.checked);
57816                 }
57817         }
57818         return true;
57819     },
57820
57821     beforeColMenuShow : function(){
57822         var cm = this.cm,  colCount = cm.getColumnCount();
57823         this.colMenu.removeAll();
57824         for(var i = 0; i < colCount; i++){
57825             this.colMenu.add(new Roo.menu.CheckItem({
57826                 id: "col-"+cm.getColumnId(i),
57827                 text: cm.getColumnHeader(i),
57828                 checked: !cm.isHidden(i),
57829                 hideOnClick:false
57830             }));
57831         }
57832     },
57833
57834     handleHdCtx : function(g, index, e){
57835         e.stopEvent();
57836         var hd = this.getHeaderCell(index);
57837         this.hdCtxIndex = index;
57838         var ms = this.hmenu.items, cm = this.cm;
57839         ms.get("asc").setDisabled(!cm.isSortable(index));
57840         ms.get("desc").setDisabled(!cm.isSortable(index));
57841         if(this.grid.enableColLock !== false){
57842             ms.get("lock").setDisabled(cm.isLocked(index));
57843             ms.get("unlock").setDisabled(!cm.isLocked(index));
57844         }
57845         this.hmenu.show(hd, "tl-bl");
57846     },
57847
57848     handleHdOver : function(e){
57849         var hd = this.findHeaderCell(e.getTarget());
57850         if(hd && !this.headersDisabled){
57851             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57852                this.fly(hd).addClass("x-grid-hd-over");
57853             }
57854         }
57855     },
57856
57857     handleHdOut : function(e){
57858         var hd = this.findHeaderCell(e.getTarget());
57859         if(hd){
57860             this.fly(hd).removeClass("x-grid-hd-over");
57861         }
57862     },
57863
57864     handleSplitDblClick : function(e, t){
57865         var i = this.getCellIndex(t);
57866         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57867             this.autoSizeColumn(i, true);
57868             this.layout();
57869         }
57870     },
57871
57872     render : function(){
57873
57874         var cm = this.cm;
57875         var colCount = cm.getColumnCount();
57876
57877         if(this.grid.monitorWindowResize === true){
57878             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57879         }
57880         var header = this.renderHeaders();
57881         var body = this.templates.body.apply({rows:""});
57882         var html = this.templates.master.apply({
57883             lockedBody: body,
57884             body: body,
57885             lockedHeader: header[0],
57886             header: header[1]
57887         });
57888
57889         //this.updateColumns();
57890
57891         this.grid.getGridEl().dom.innerHTML = html;
57892
57893         this.initElements();
57894         
57895         // a kludge to fix the random scolling effect in webkit
57896         this.el.on("scroll", function() {
57897             this.el.dom.scrollTop=0; // hopefully not recursive..
57898         },this);
57899
57900         this.scroller.on("scroll", this.handleScroll, this);
57901         this.lockedBody.on("mousewheel", this.handleWheel, this);
57902         this.mainBody.on("mousewheel", this.handleWheel, this);
57903
57904         this.mainHd.on("mouseover", this.handleHdOver, this);
57905         this.mainHd.on("mouseout", this.handleHdOut, this);
57906         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57907                 {delegate: "."+this.splitClass});
57908
57909         this.lockedHd.on("mouseover", this.handleHdOver, this);
57910         this.lockedHd.on("mouseout", this.handleHdOut, this);
57911         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57912                 {delegate: "."+this.splitClass});
57913
57914         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57915             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57916         }
57917
57918         this.updateSplitters();
57919
57920         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57921             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57922             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57923         }
57924
57925         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57926             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57927             this.hmenu.add(
57928                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57929                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57930             );
57931             if(this.grid.enableColLock !== false){
57932                 this.hmenu.add('-',
57933                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57934                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57935                 );
57936             }
57937             if (Roo.isTouch) {
57938                  this.hmenu.add('-',
57939                     {id:"wider", text: this.columnsWiderText},
57940                     {id:"narrow", text: this.columnsNarrowText }
57941                 );
57942                 
57943                  
57944             }
57945             
57946             if(this.grid.enableColumnHide !== false){
57947
57948                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57949                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57950                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57951
57952                 this.hmenu.add('-',
57953                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57954                 );
57955             }
57956             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57957
57958             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57959         }
57960
57961         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57962             this.dd = new Roo.grid.GridDragZone(this.grid, {
57963                 ddGroup : this.grid.ddGroup || 'GridDD'
57964             });
57965             
57966         }
57967
57968         /*
57969         for(var i = 0; i < colCount; i++){
57970             if(cm.isHidden(i)){
57971                 this.hideColumn(i);
57972             }
57973             if(cm.config[i].align){
57974                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57975                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57976             }
57977         }*/
57978         
57979         this.updateHeaderSortState();
57980
57981         this.beforeInitialResize();
57982         this.layout(true);
57983
57984         // two part rendering gives faster view to the user
57985         this.renderPhase2.defer(1, this);
57986     },
57987
57988     renderPhase2 : function(){
57989         // render the rows now
57990         this.refresh();
57991         if(this.grid.autoSizeColumns){
57992             this.autoSizeColumns();
57993         }
57994     },
57995
57996     beforeInitialResize : function(){
57997
57998     },
57999
58000     onColumnSplitterMoved : function(i, w){
58001         this.userResized = true;
58002         var cm = this.grid.colModel;
58003         cm.setColumnWidth(i, w, true);
58004         var cid = cm.getColumnId(i);
58005         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58006         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58007         this.updateSplitters();
58008         this.layout();
58009         this.grid.fireEvent("columnresize", i, w);
58010     },
58011
58012     syncRowHeights : function(startIndex, endIndex){
58013         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58014             startIndex = startIndex || 0;
58015             var mrows = this.getBodyTable().rows;
58016             var lrows = this.getLockedTable().rows;
58017             var len = mrows.length-1;
58018             endIndex = Math.min(endIndex || len, len);
58019             for(var i = startIndex; i <= endIndex; i++){
58020                 var m = mrows[i], l = lrows[i];
58021                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58022                 m.style.height = l.style.height = h + "px";
58023             }
58024         }
58025     },
58026
58027     layout : function(initialRender, is2ndPass)
58028     {
58029         var g = this.grid;
58030         var auto = g.autoHeight;
58031         var scrollOffset = 16;
58032         var c = g.getGridEl(), cm = this.cm,
58033                 expandCol = g.autoExpandColumn,
58034                 gv = this;
58035         //c.beginMeasure();
58036
58037         if(!c.dom.offsetWidth){ // display:none?
58038             if(initialRender){
58039                 this.lockedWrap.show();
58040                 this.mainWrap.show();
58041             }
58042             return;
58043         }
58044
58045         var hasLock = this.cm.isLocked(0);
58046
58047         var tbh = this.headerPanel.getHeight();
58048         var bbh = this.footerPanel.getHeight();
58049
58050         if(auto){
58051             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58052             var newHeight = ch + c.getBorderWidth("tb");
58053             if(g.maxHeight){
58054                 newHeight = Math.min(g.maxHeight, newHeight);
58055             }
58056             c.setHeight(newHeight);
58057         }
58058
58059         if(g.autoWidth){
58060             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58061         }
58062
58063         var s = this.scroller;
58064
58065         var csize = c.getSize(true);
58066
58067         this.el.setSize(csize.width, csize.height);
58068
58069         this.headerPanel.setWidth(csize.width);
58070         this.footerPanel.setWidth(csize.width);
58071
58072         var hdHeight = this.mainHd.getHeight();
58073         var vw = csize.width;
58074         var vh = csize.height - (tbh + bbh);
58075
58076         s.setSize(vw, vh);
58077
58078         var bt = this.getBodyTable();
58079         
58080         if(cm.getLockedCount() == cm.config.length){
58081             bt = this.getLockedTable();
58082         }
58083         
58084         var ltWidth = hasLock ?
58085                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58086
58087         var scrollHeight = bt.offsetHeight;
58088         var scrollWidth = ltWidth + bt.offsetWidth;
58089         var vscroll = false, hscroll = false;
58090
58091         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58092
58093         var lw = this.lockedWrap, mw = this.mainWrap;
58094         var lb = this.lockedBody, mb = this.mainBody;
58095
58096         setTimeout(function(){
58097             var t = s.dom.offsetTop;
58098             var w = s.dom.clientWidth,
58099                 h = s.dom.clientHeight;
58100
58101             lw.setTop(t);
58102             lw.setSize(ltWidth, h);
58103
58104             mw.setLeftTop(ltWidth, t);
58105             mw.setSize(w-ltWidth, h);
58106
58107             lb.setHeight(h-hdHeight);
58108             mb.setHeight(h-hdHeight);
58109
58110             if(is2ndPass !== true && !gv.userResized && expandCol){
58111                 // high speed resize without full column calculation
58112                 
58113                 var ci = cm.getIndexById(expandCol);
58114                 if (ci < 0) {
58115                     ci = cm.findColumnIndex(expandCol);
58116                 }
58117                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58118                 var expandId = cm.getColumnId(ci);
58119                 var  tw = cm.getTotalWidth(false);
58120                 var currentWidth = cm.getColumnWidth(ci);
58121                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58122                 if(currentWidth != cw){
58123                     cm.setColumnWidth(ci, cw, true);
58124                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58125                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58126                     gv.updateSplitters();
58127                     gv.layout(false, true);
58128                 }
58129             }
58130
58131             if(initialRender){
58132                 lw.show();
58133                 mw.show();
58134             }
58135             //c.endMeasure();
58136         }, 10);
58137     },
58138
58139     onWindowResize : function(){
58140         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58141             return;
58142         }
58143         this.layout();
58144     },
58145
58146     appendFooter : function(parentEl){
58147         return null;
58148     },
58149
58150     sortAscText : "Sort Ascending",
58151     sortDescText : "Sort Descending",
58152     lockText : "Lock Column",
58153     unlockText : "Unlock Column",
58154     columnsText : "Columns",
58155  
58156     columnsWiderText : "Wider",
58157     columnsNarrowText : "Thinner"
58158 });
58159
58160
58161 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58162     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58163     this.proxy.el.addClass('x-grid3-col-dd');
58164 };
58165
58166 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58167     handleMouseDown : function(e){
58168
58169     },
58170
58171     callHandleMouseDown : function(e){
58172         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58173     }
58174 });
58175 /*
58176  * Based on:
58177  * Ext JS Library 1.1.1
58178  * Copyright(c) 2006-2007, Ext JS, LLC.
58179  *
58180  * Originally Released Under LGPL - original licence link has changed is not relivant.
58181  *
58182  * Fork - LGPL
58183  * <script type="text/javascript">
58184  */
58185  /**
58186  * @extends Roo.dd.DDProxy
58187  * @class Roo.grid.SplitDragZone
58188  * Support for Column Header resizing
58189  * @constructor
58190  * @param {Object} config
58191  */
58192 // private
58193 // This is a support class used internally by the Grid components
58194 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58195     this.grid = grid;
58196     this.view = grid.getView();
58197     this.proxy = this.view.resizeProxy;
58198     Roo.grid.SplitDragZone.superclass.constructor.call(
58199         this,
58200         hd, // ID
58201         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58202         {  // CONFIG
58203             dragElId : Roo.id(this.proxy.dom),
58204             resizeFrame:false
58205         }
58206     );
58207     
58208     this.setHandleElId(Roo.id(hd));
58209     if (hd2 !== false) {
58210         this.setOuterHandleElId(Roo.id(hd2));
58211     }
58212     
58213     this.scroll = false;
58214 };
58215 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58216     fly: Roo.Element.fly,
58217
58218     b4StartDrag : function(x, y){
58219         this.view.headersDisabled = true;
58220         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58221                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58222         );
58223         this.proxy.setHeight(h);
58224         
58225         // for old system colWidth really stored the actual width?
58226         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58227         // which in reality did not work.. - it worked only for fixed sizes
58228         // for resizable we need to use actual sizes.
58229         var w = this.cm.getColumnWidth(this.cellIndex);
58230         if (!this.view.mainWrap) {
58231             // bootstrap.
58232             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58233         }
58234         
58235         
58236         
58237         // this was w-this.grid.minColumnWidth;
58238         // doesnt really make sense? - w = thie curren width or the rendered one?
58239         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58240         this.resetConstraints();
58241         this.setXConstraint(minw, 1000);
58242         this.setYConstraint(0, 0);
58243         this.minX = x - minw;
58244         this.maxX = x + 1000;
58245         this.startPos = x;
58246         if (!this.view.mainWrap) { // this is Bootstrap code..
58247             this.getDragEl().style.display='block';
58248         }
58249         
58250         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58251     },
58252
58253
58254     handleMouseDown : function(e){
58255         ev = Roo.EventObject.setEvent(e);
58256         var t = this.fly(ev.getTarget());
58257         if(t.hasClass("x-grid-split")){
58258             this.cellIndex = this.view.getCellIndex(t.dom);
58259             this.split = t.dom;
58260             this.cm = this.grid.colModel;
58261             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58262                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58263             }
58264         }
58265     },
58266
58267     endDrag : function(e){
58268         this.view.headersDisabled = false;
58269         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58270         var diff = endX - this.startPos;
58271         // 
58272         var w = this.cm.getColumnWidth(this.cellIndex);
58273         if (!this.view.mainWrap) {
58274             w = 0;
58275         }
58276         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58277     },
58278
58279     autoOffset : function(){
58280         this.setDelta(0,0);
58281     }
58282 });/*
58283  * Based on:
58284  * Ext JS Library 1.1.1
58285  * Copyright(c) 2006-2007, Ext JS, LLC.
58286  *
58287  * Originally Released Under LGPL - original licence link has changed is not relivant.
58288  *
58289  * Fork - LGPL
58290  * <script type="text/javascript">
58291  */
58292  
58293 // private
58294 // This is a support class used internally by the Grid components
58295 Roo.grid.GridDragZone = function(grid, config){
58296     this.view = grid.getView();
58297     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58298     if(this.view.lockedBody){
58299         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58300         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58301     }
58302     this.scroll = false;
58303     this.grid = grid;
58304     this.ddel = document.createElement('div');
58305     this.ddel.className = 'x-grid-dd-wrap';
58306 };
58307
58308 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58309     ddGroup : "GridDD",
58310
58311     getDragData : function(e){
58312         var t = Roo.lib.Event.getTarget(e);
58313         var rowIndex = this.view.findRowIndex(t);
58314         var sm = this.grid.selModel;
58315             
58316         //Roo.log(rowIndex);
58317         
58318         if (sm.getSelectedCell) {
58319             // cell selection..
58320             if (!sm.getSelectedCell()) {
58321                 return false;
58322             }
58323             if (rowIndex != sm.getSelectedCell()[0]) {
58324                 return false;
58325             }
58326         
58327         }
58328         if (sm.getSelections && sm.getSelections().length < 1) {
58329             return false;
58330         }
58331         
58332         
58333         // before it used to all dragging of unseleted... - now we dont do that.
58334         if(rowIndex !== false){
58335             
58336             // if editorgrid.. 
58337             
58338             
58339             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58340                
58341             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58342               //  
58343             //}
58344             if (e.hasModifier()){
58345                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58346             }
58347             
58348             Roo.log("getDragData");
58349             
58350             return {
58351                 grid: this.grid,
58352                 ddel: this.ddel,
58353                 rowIndex: rowIndex,
58354                 selections: sm.getSelections ? sm.getSelections() : (
58355                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58356             };
58357         }
58358         return false;
58359     },
58360     
58361     
58362     onInitDrag : function(e){
58363         var data = this.dragData;
58364         this.ddel.innerHTML = this.grid.getDragDropText();
58365         this.proxy.update(this.ddel);
58366         // fire start drag?
58367     },
58368
58369     afterRepair : function(){
58370         this.dragging = false;
58371     },
58372
58373     getRepairXY : function(e, data){
58374         return false;
58375     },
58376
58377     onEndDrag : function(data, e){
58378         // fire end drag?
58379     },
58380
58381     onValidDrop : function(dd, e, id){
58382         // fire drag drop?
58383         this.hideProxy();
58384     },
58385
58386     beforeInvalidDrop : function(e, id){
58387
58388     }
58389 });/*
58390  * Based on:
58391  * Ext JS Library 1.1.1
58392  * Copyright(c) 2006-2007, Ext JS, LLC.
58393  *
58394  * Originally Released Under LGPL - original licence link has changed is not relivant.
58395  *
58396  * Fork - LGPL
58397  * <script type="text/javascript">
58398  */
58399  
58400
58401 /**
58402  * @class Roo.grid.ColumnModel
58403  * @extends Roo.util.Observable
58404  * This is the default implementation of a ColumnModel used by the Grid. It defines
58405  * the columns in the grid.
58406  * <br>Usage:<br>
58407  <pre><code>
58408  var colModel = new Roo.grid.ColumnModel([
58409         {header: "Ticker", width: 60, sortable: true, locked: true},
58410         {header: "Company Name", width: 150, sortable: true},
58411         {header: "Market Cap.", width: 100, sortable: true},
58412         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58413         {header: "Employees", width: 100, sortable: true, resizable: false}
58414  ]);
58415  </code></pre>
58416  * <p>
58417  
58418  * The config options listed for this class are options which may appear in each
58419  * individual column definition.
58420  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58421  * @constructor
58422  * @param {Object} config An Array of column config objects. See this class's
58423  * config objects for details.
58424 */
58425 Roo.grid.ColumnModel = function(config){
58426         /**
58427      * The config passed into the constructor
58428      */
58429     this.config = []; //config;
58430     this.lookup = {};
58431
58432     // if no id, create one
58433     // if the column does not have a dataIndex mapping,
58434     // map it to the order it is in the config
58435     for(var i = 0, len = config.length; i < len; i++){
58436         this.addColumn(config[i]);
58437         
58438     }
58439
58440     /**
58441      * The width of columns which have no width specified (defaults to 100)
58442      * @type Number
58443      */
58444     this.defaultWidth = 100;
58445
58446     /**
58447      * Default sortable of columns which have no sortable specified (defaults to false)
58448      * @type Boolean
58449      */
58450     this.defaultSortable = false;
58451
58452     this.addEvents({
58453         /**
58454              * @event widthchange
58455              * Fires when the width of a column changes.
58456              * @param {ColumnModel} this
58457              * @param {Number} columnIndex The column index
58458              * @param {Number} newWidth The new width
58459              */
58460             "widthchange": true,
58461         /**
58462              * @event headerchange
58463              * Fires when the text of a header changes.
58464              * @param {ColumnModel} this
58465              * @param {Number} columnIndex The column index
58466              * @param {Number} newText The new header text
58467              */
58468             "headerchange": true,
58469         /**
58470              * @event hiddenchange
58471              * Fires when a column is hidden or "unhidden".
58472              * @param {ColumnModel} this
58473              * @param {Number} columnIndex The column index
58474              * @param {Boolean} hidden true if hidden, false otherwise
58475              */
58476             "hiddenchange": true,
58477             /**
58478          * @event columnmoved
58479          * Fires when a column is moved.
58480          * @param {ColumnModel} this
58481          * @param {Number} oldIndex
58482          * @param {Number} newIndex
58483          */
58484         "columnmoved" : true,
58485         /**
58486          * @event columlockchange
58487          * Fires when a column's locked state is changed
58488          * @param {ColumnModel} this
58489          * @param {Number} colIndex
58490          * @param {Boolean} locked true if locked
58491          */
58492         "columnlockchange" : true
58493     });
58494     Roo.grid.ColumnModel.superclass.constructor.call(this);
58495 };
58496 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58497     /**
58498      * @cfg {String} header The header text to display in the Grid view.
58499      */
58500         /**
58501      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58502      */
58503         /**
58504      * @cfg {String} smHeader Header at Bootsrap Small width
58505      */
58506         /**
58507      * @cfg {String} mdHeader Header at Bootsrap Medium width
58508      */
58509         /**
58510      * @cfg {String} lgHeader Header at Bootsrap Large width
58511      */
58512         /**
58513      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58514      */
58515     /**
58516      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58517      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58518      * specified, the column's index is used as an index into the Record's data Array.
58519      */
58520     /**
58521      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58522      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58523      */
58524     /**
58525      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58526      * Defaults to the value of the {@link #defaultSortable} property.
58527      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58528      */
58529     /**
58530      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58531      */
58532     /**
58533      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58534      */
58535     /**
58536      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58537      */
58538     /**
58539      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58540      */
58541     /**
58542      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58543      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58544      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58545      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58546      */
58547        /**
58548      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58549      */
58550     /**
58551      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58552      */
58553     /**
58554      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58555      */
58556     /**
58557      * @cfg {String} cursor (Optional)
58558      */
58559     /**
58560      * @cfg {String} tooltip (Optional)
58561      */
58562     /**
58563      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58564      */
58565     /**
58566      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58567      */
58568     /**
58569      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58570      */
58571     /**
58572      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58573      */
58574         /**
58575      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58576      */
58577     /**
58578      * Returns the id of the column at the specified index.
58579      * @param {Number} index The column index
58580      * @return {String} the id
58581      */
58582     getColumnId : function(index){
58583         return this.config[index].id;
58584     },
58585
58586     /**
58587      * Returns the column for a specified id.
58588      * @param {String} id The column id
58589      * @return {Object} the column
58590      */
58591     getColumnById : function(id){
58592         return this.lookup[id];
58593     },
58594
58595     
58596     /**
58597      * Returns the column Object for a specified dataIndex.
58598      * @param {String} dataIndex The column dataIndex
58599      * @return {Object|Boolean} the column or false if not found
58600      */
58601     getColumnByDataIndex: function(dataIndex){
58602         var index = this.findColumnIndex(dataIndex);
58603         return index > -1 ? this.config[index] : false;
58604     },
58605     
58606     /**
58607      * Returns the index for a specified column id.
58608      * @param {String} id The column id
58609      * @return {Number} the index, or -1 if not found
58610      */
58611     getIndexById : function(id){
58612         for(var i = 0, len = this.config.length; i < len; i++){
58613             if(this.config[i].id == id){
58614                 return i;
58615             }
58616         }
58617         return -1;
58618     },
58619     
58620     /**
58621      * Returns the index for a specified column dataIndex.
58622      * @param {String} dataIndex The column dataIndex
58623      * @return {Number} the index, or -1 if not found
58624      */
58625     
58626     findColumnIndex : function(dataIndex){
58627         for(var i = 0, len = this.config.length; i < len; i++){
58628             if(this.config[i].dataIndex == dataIndex){
58629                 return i;
58630             }
58631         }
58632         return -1;
58633     },
58634     
58635     
58636     moveColumn : function(oldIndex, newIndex){
58637         var c = this.config[oldIndex];
58638         this.config.splice(oldIndex, 1);
58639         this.config.splice(newIndex, 0, c);
58640         this.dataMap = null;
58641         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58642     },
58643
58644     isLocked : function(colIndex){
58645         return this.config[colIndex].locked === true;
58646     },
58647
58648     setLocked : function(colIndex, value, suppressEvent){
58649         if(this.isLocked(colIndex) == value){
58650             return;
58651         }
58652         this.config[colIndex].locked = value;
58653         if(!suppressEvent){
58654             this.fireEvent("columnlockchange", this, colIndex, value);
58655         }
58656     },
58657
58658     getTotalLockedWidth : function(){
58659         var totalWidth = 0;
58660         for(var i = 0; i < this.config.length; i++){
58661             if(this.isLocked(i) && !this.isHidden(i)){
58662                 this.totalWidth += this.getColumnWidth(i);
58663             }
58664         }
58665         return totalWidth;
58666     },
58667
58668     getLockedCount : function(){
58669         for(var i = 0, len = this.config.length; i < len; i++){
58670             if(!this.isLocked(i)){
58671                 return i;
58672             }
58673         }
58674         
58675         return this.config.length;
58676     },
58677
58678     /**
58679      * Returns the number of columns.
58680      * @return {Number}
58681      */
58682     getColumnCount : function(visibleOnly){
58683         if(visibleOnly === true){
58684             var c = 0;
58685             for(var i = 0, len = this.config.length; i < len; i++){
58686                 if(!this.isHidden(i)){
58687                     c++;
58688                 }
58689             }
58690             return c;
58691         }
58692         return this.config.length;
58693     },
58694
58695     /**
58696      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58697      * @param {Function} fn
58698      * @param {Object} scope (optional)
58699      * @return {Array} result
58700      */
58701     getColumnsBy : function(fn, scope){
58702         var r = [];
58703         for(var i = 0, len = this.config.length; i < len; i++){
58704             var c = this.config[i];
58705             if(fn.call(scope||this, c, i) === true){
58706                 r[r.length] = c;
58707             }
58708         }
58709         return r;
58710     },
58711
58712     /**
58713      * Returns true if the specified column is sortable.
58714      * @param {Number} col The column index
58715      * @return {Boolean}
58716      */
58717     isSortable : function(col){
58718         if(typeof this.config[col].sortable == "undefined"){
58719             return this.defaultSortable;
58720         }
58721         return this.config[col].sortable;
58722     },
58723
58724     /**
58725      * Returns the rendering (formatting) function defined for the column.
58726      * @param {Number} col The column index.
58727      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58728      */
58729     getRenderer : function(col){
58730         if(!this.config[col].renderer){
58731             return Roo.grid.ColumnModel.defaultRenderer;
58732         }
58733         return this.config[col].renderer;
58734     },
58735
58736     /**
58737      * Sets the rendering (formatting) function for a column.
58738      * @param {Number} col The column index
58739      * @param {Function} fn The function to use to process the cell's raw data
58740      * to return HTML markup for the grid view. The render function is called with
58741      * the following parameters:<ul>
58742      * <li>Data value.</li>
58743      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58744      * <li>css A CSS style string to apply to the table cell.</li>
58745      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58746      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58747      * <li>Row index</li>
58748      * <li>Column index</li>
58749      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58750      */
58751     setRenderer : function(col, fn){
58752         this.config[col].renderer = fn;
58753     },
58754
58755     /**
58756      * Returns the width for the specified column.
58757      * @param {Number} col The column index
58758      * @param (optional) {String} gridSize bootstrap width size.
58759      * @return {Number}
58760      */
58761     getColumnWidth : function(col, gridSize)
58762         {
58763                 var cfg = this.config[col];
58764                 
58765                 if (typeof(gridSize) == 'undefined') {
58766                         return cfg.width * 1 || this.defaultWidth;
58767                 }
58768                 if (gridSize === false) { // if we set it..
58769                         return cfg.width || false;
58770                 }
58771                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58772                 
58773                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58774                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58775                                 continue;
58776                         }
58777                         return cfg[ sizes[i] ];
58778                 }
58779                 return 1;
58780                 
58781     },
58782
58783     /**
58784      * Sets the width for a column.
58785      * @param {Number} col The column index
58786      * @param {Number} width The new width
58787      */
58788     setColumnWidth : function(col, width, suppressEvent){
58789         this.config[col].width = width;
58790         this.totalWidth = null;
58791         if(!suppressEvent){
58792              this.fireEvent("widthchange", this, col, width);
58793         }
58794     },
58795
58796     /**
58797      * Returns the total width of all columns.
58798      * @param {Boolean} includeHidden True to include hidden column widths
58799      * @return {Number}
58800      */
58801     getTotalWidth : function(includeHidden){
58802         if(!this.totalWidth){
58803             this.totalWidth = 0;
58804             for(var i = 0, len = this.config.length; i < len; i++){
58805                 if(includeHidden || !this.isHidden(i)){
58806                     this.totalWidth += this.getColumnWidth(i);
58807                 }
58808             }
58809         }
58810         return this.totalWidth;
58811     },
58812
58813     /**
58814      * Returns the header for the specified column.
58815      * @param {Number} col The column index
58816      * @return {String}
58817      */
58818     getColumnHeader : function(col){
58819         return this.config[col].header;
58820     },
58821
58822     /**
58823      * Sets the header for a column.
58824      * @param {Number} col The column index
58825      * @param {String} header The new header
58826      */
58827     setColumnHeader : function(col, header){
58828         this.config[col].header = header;
58829         this.fireEvent("headerchange", this, col, header);
58830     },
58831
58832     /**
58833      * Returns the tooltip for the specified column.
58834      * @param {Number} col The column index
58835      * @return {String}
58836      */
58837     getColumnTooltip : function(col){
58838             return this.config[col].tooltip;
58839     },
58840     /**
58841      * Sets the tooltip for a column.
58842      * @param {Number} col The column index
58843      * @param {String} tooltip The new tooltip
58844      */
58845     setColumnTooltip : function(col, tooltip){
58846             this.config[col].tooltip = tooltip;
58847     },
58848
58849     /**
58850      * Returns the dataIndex for the specified column.
58851      * @param {Number} col The column index
58852      * @return {Number}
58853      */
58854     getDataIndex : function(col){
58855         return this.config[col].dataIndex;
58856     },
58857
58858     /**
58859      * Sets the dataIndex for a column.
58860      * @param {Number} col The column index
58861      * @param {Number} dataIndex The new dataIndex
58862      */
58863     setDataIndex : function(col, dataIndex){
58864         this.config[col].dataIndex = dataIndex;
58865     },
58866
58867     
58868     
58869     /**
58870      * Returns true if the cell is editable.
58871      * @param {Number} colIndex The column index
58872      * @param {Number} rowIndex The row index - this is nto actually used..?
58873      * @return {Boolean}
58874      */
58875     isCellEditable : function(colIndex, rowIndex){
58876         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58877     },
58878
58879     /**
58880      * Returns the editor defined for the cell/column.
58881      * return false or null to disable editing.
58882      * @param {Number} colIndex The column index
58883      * @param {Number} rowIndex The row index
58884      * @return {Object}
58885      */
58886     getCellEditor : function(colIndex, rowIndex){
58887         return this.config[colIndex].editor;
58888     },
58889
58890     /**
58891      * Sets if a column is editable.
58892      * @param {Number} col The column index
58893      * @param {Boolean} editable True if the column is editable
58894      */
58895     setEditable : function(col, editable){
58896         this.config[col].editable = editable;
58897     },
58898
58899
58900     /**
58901      * Returns true if the column is hidden.
58902      * @param {Number} colIndex The column index
58903      * @return {Boolean}
58904      */
58905     isHidden : function(colIndex){
58906         return this.config[colIndex].hidden;
58907     },
58908
58909
58910     /**
58911      * Returns true if the column width cannot be changed
58912      */
58913     isFixed : function(colIndex){
58914         return this.config[colIndex].fixed;
58915     },
58916
58917     /**
58918      * Returns true if the column can be resized
58919      * @return {Boolean}
58920      */
58921     isResizable : function(colIndex){
58922         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58923     },
58924     /**
58925      * Sets if a column is hidden.
58926      * @param {Number} colIndex The column index
58927      * @param {Boolean} hidden True if the column is hidden
58928      */
58929     setHidden : function(colIndex, hidden){
58930         this.config[colIndex].hidden = hidden;
58931         this.totalWidth = null;
58932         this.fireEvent("hiddenchange", this, colIndex, hidden);
58933     },
58934
58935     /**
58936      * Sets the editor for a column.
58937      * @param {Number} col The column index
58938      * @param {Object} editor The editor object
58939      */
58940     setEditor : function(col, editor){
58941         this.config[col].editor = editor;
58942     },
58943     /**
58944      * Add a column (experimental...) - defaults to adding to the end..
58945      * @param {Object} config 
58946     */
58947     addColumn : function(c)
58948     {
58949     
58950         var i = this.config.length;
58951         this.config[i] = c;
58952         
58953         if(typeof c.dataIndex == "undefined"){
58954             c.dataIndex = i;
58955         }
58956         if(typeof c.renderer == "string"){
58957             c.renderer = Roo.util.Format[c.renderer];
58958         }
58959         if(typeof c.id == "undefined"){
58960             c.id = Roo.id();
58961         }
58962         if(c.editor && c.editor.xtype){
58963             c.editor  = Roo.factory(c.editor, Roo.grid);
58964         }
58965         if(c.editor && c.editor.isFormField){
58966             c.editor = new Roo.grid.GridEditor(c.editor);
58967         }
58968         this.lookup[c.id] = c;
58969     }
58970     
58971 });
58972
58973 Roo.grid.ColumnModel.defaultRenderer = function(value)
58974 {
58975     if(typeof value == "object") {
58976         return value;
58977     }
58978         if(typeof value == "string" && value.length < 1){
58979             return "&#160;";
58980         }
58981     
58982         return String.format("{0}", value);
58983 };
58984
58985 // Alias for backwards compatibility
58986 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58987 /*
58988  * Based on:
58989  * Ext JS Library 1.1.1
58990  * Copyright(c) 2006-2007, Ext JS, LLC.
58991  *
58992  * Originally Released Under LGPL - original licence link has changed is not relivant.
58993  *
58994  * Fork - LGPL
58995  * <script type="text/javascript">
58996  */
58997
58998 /**
58999  * @class Roo.grid.AbstractSelectionModel
59000  * @extends Roo.util.Observable
59001  * @abstract
59002  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59003  * implemented by descendant classes.  This class should not be directly instantiated.
59004  * @constructor
59005  */
59006 Roo.grid.AbstractSelectionModel = function(){
59007     this.locked = false;
59008     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59009 };
59010
59011 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59012     /** @ignore Called by the grid automatically. Do not call directly. */
59013     init : function(grid){
59014         this.grid = grid;
59015         this.initEvents();
59016     },
59017
59018     /**
59019      * Locks the selections.
59020      */
59021     lock : function(){
59022         this.locked = true;
59023     },
59024
59025     /**
59026      * Unlocks the selections.
59027      */
59028     unlock : function(){
59029         this.locked = false;
59030     },
59031
59032     /**
59033      * Returns true if the selections are locked.
59034      * @return {Boolean}
59035      */
59036     isLocked : function(){
59037         return this.locked;
59038     }
59039 });/*
59040  * Based on:
59041  * Ext JS Library 1.1.1
59042  * Copyright(c) 2006-2007, Ext JS, LLC.
59043  *
59044  * Originally Released Under LGPL - original licence link has changed is not relivant.
59045  *
59046  * Fork - LGPL
59047  * <script type="text/javascript">
59048  */
59049 /**
59050  * @extends Roo.grid.AbstractSelectionModel
59051  * @class Roo.grid.RowSelectionModel
59052  * The default SelectionModel used by {@link Roo.grid.Grid}.
59053  * It supports multiple selections and keyboard selection/navigation. 
59054  * @constructor
59055  * @param {Object} config
59056  */
59057 Roo.grid.RowSelectionModel = function(config){
59058     Roo.apply(this, config);
59059     this.selections = new Roo.util.MixedCollection(false, function(o){
59060         return o.id;
59061     });
59062
59063     this.last = false;
59064     this.lastActive = false;
59065
59066     this.addEvents({
59067         /**
59068         * @event selectionchange
59069         * Fires when the selection changes
59070         * @param {SelectionModel} this
59071         */
59072        "selectionchange" : true,
59073        /**
59074         * @event afterselectionchange
59075         * Fires after the selection changes (eg. by key press or clicking)
59076         * @param {SelectionModel} this
59077         */
59078        "afterselectionchange" : true,
59079        /**
59080         * @event beforerowselect
59081         * Fires when a row is selected being selected, return false to cancel.
59082         * @param {SelectionModel} this
59083         * @param {Number} rowIndex The selected index
59084         * @param {Boolean} keepExisting False if other selections will be cleared
59085         */
59086        "beforerowselect" : true,
59087        /**
59088         * @event rowselect
59089         * Fires when a row is selected.
59090         * @param {SelectionModel} this
59091         * @param {Number} rowIndex The selected index
59092         * @param {Roo.data.Record} r The record
59093         */
59094        "rowselect" : true,
59095        /**
59096         * @event rowdeselect
59097         * Fires when a row is deselected.
59098         * @param {SelectionModel} this
59099         * @param {Number} rowIndex The selected index
59100         */
59101         "rowdeselect" : true
59102     });
59103     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59104     this.locked = false;
59105 };
59106
59107 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59108     /**
59109      * @cfg {Boolean} singleSelect
59110      * True to allow selection of only one row at a time (defaults to false)
59111      */
59112     singleSelect : false,
59113
59114     // private
59115     initEvents : function(){
59116
59117         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59118             this.grid.on("mousedown", this.handleMouseDown, this);
59119         }else{ // allow click to work like normal
59120             this.grid.on("rowclick", this.handleDragableRowClick, this);
59121         }
59122         // bootstrap does not have a view..
59123         var view = this.grid.view ? this.grid.view : this.grid;
59124         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59125             "up" : function(e){
59126                 if(!e.shiftKey){
59127                     this.selectPrevious(e.shiftKey);
59128                 }else if(this.last !== false && this.lastActive !== false){
59129                     var last = this.last;
59130                     this.selectRange(this.last,  this.lastActive-1);
59131                     view.focusRow(this.lastActive);
59132                     if(last !== false){
59133                         this.last = last;
59134                     }
59135                 }else{
59136                     this.selectFirstRow();
59137                 }
59138                 this.fireEvent("afterselectionchange", this);
59139             },
59140             "down" : function(e){
59141                 if(!e.shiftKey){
59142                     this.selectNext(e.shiftKey);
59143                 }else if(this.last !== false && this.lastActive !== false){
59144                     var last = this.last;
59145                     this.selectRange(this.last,  this.lastActive+1);
59146                     view.focusRow(this.lastActive);
59147                     if(last !== false){
59148                         this.last = last;
59149                     }
59150                 }else{
59151                     this.selectFirstRow();
59152                 }
59153                 this.fireEvent("afterselectionchange", this);
59154             },
59155             scope: this
59156         });
59157
59158          
59159         view.on("refresh", this.onRefresh, this);
59160         view.on("rowupdated", this.onRowUpdated, this);
59161         view.on("rowremoved", this.onRemove, this);
59162     },
59163
59164     // private
59165     onRefresh : function(){
59166         var ds = this.grid.ds, i, v = this.grid.view;
59167         var s = this.selections;
59168         s.each(function(r){
59169             if((i = ds.indexOfId(r.id)) != -1){
59170                 v.onRowSelect(i);
59171                 s.add(ds.getAt(i)); // updating the selection relate data
59172             }else{
59173                 s.remove(r);
59174             }
59175         });
59176     },
59177
59178     // private
59179     onRemove : function(v, index, r){
59180         this.selections.remove(r);
59181     },
59182
59183     // private
59184     onRowUpdated : function(v, index, r){
59185         if(this.isSelected(r)){
59186             v.onRowSelect(index);
59187         }
59188     },
59189
59190     /**
59191      * Select records.
59192      * @param {Array} records The records to select
59193      * @param {Boolean} keepExisting (optional) True to keep existing selections
59194      */
59195     selectRecords : function(records, keepExisting){
59196         if(!keepExisting){
59197             this.clearSelections();
59198         }
59199         var ds = this.grid.ds;
59200         for(var i = 0, len = records.length; i < len; i++){
59201             this.selectRow(ds.indexOf(records[i]), true);
59202         }
59203     },
59204
59205     /**
59206      * Gets the number of selected rows.
59207      * @return {Number}
59208      */
59209     getCount : function(){
59210         return this.selections.length;
59211     },
59212
59213     /**
59214      * Selects the first row in the grid.
59215      */
59216     selectFirstRow : function(){
59217         this.selectRow(0);
59218     },
59219
59220     /**
59221      * Select the last row.
59222      * @param {Boolean} keepExisting (optional) True to keep existing selections
59223      */
59224     selectLastRow : function(keepExisting){
59225         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59226     },
59227
59228     /**
59229      * Selects the row immediately following the last selected row.
59230      * @param {Boolean} keepExisting (optional) True to keep existing selections
59231      */
59232     selectNext : function(keepExisting){
59233         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59234             this.selectRow(this.last+1, keepExisting);
59235             var view = this.grid.view ? this.grid.view : this.grid;
59236             view.focusRow(this.last);
59237         }
59238     },
59239
59240     /**
59241      * Selects the row that precedes the last selected row.
59242      * @param {Boolean} keepExisting (optional) True to keep existing selections
59243      */
59244     selectPrevious : function(keepExisting){
59245         if(this.last){
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      * Returns the selected records
59254      * @return {Array} Array of selected records
59255      */
59256     getSelections : function(){
59257         return [].concat(this.selections.items);
59258     },
59259
59260     /**
59261      * Returns the first selected record.
59262      * @return {Record}
59263      */
59264     getSelected : function(){
59265         return this.selections.itemAt(0);
59266     },
59267
59268
59269     /**
59270      * Clears all selections.
59271      */
59272     clearSelections : function(fast){
59273         if(this.locked) {
59274             return;
59275         }
59276         if(fast !== true){
59277             var ds = this.grid.ds;
59278             var s = this.selections;
59279             s.each(function(r){
59280                 this.deselectRow(ds.indexOfId(r.id));
59281             }, this);
59282             s.clear();
59283         }else{
59284             this.selections.clear();
59285         }
59286         this.last = false;
59287     },
59288
59289
59290     /**
59291      * Selects all rows.
59292      */
59293     selectAll : function(){
59294         if(this.locked) {
59295             return;
59296         }
59297         this.selections.clear();
59298         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59299             this.selectRow(i, true);
59300         }
59301     },
59302
59303     /**
59304      * Returns True if there is a selection.
59305      * @return {Boolean}
59306      */
59307     hasSelection : function(){
59308         return this.selections.length > 0;
59309     },
59310
59311     /**
59312      * Returns True if the specified row is selected.
59313      * @param {Number/Record} record The record or index of the record to check
59314      * @return {Boolean}
59315      */
59316     isSelected : function(index){
59317         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59318         return (r && this.selections.key(r.id) ? true : false);
59319     },
59320
59321     /**
59322      * Returns True if the specified record id is selected.
59323      * @param {String} id The id of record to check
59324      * @return {Boolean}
59325      */
59326     isIdSelected : function(id){
59327         return (this.selections.key(id) ? true : false);
59328     },
59329
59330     // private
59331     handleMouseDown : function(e, t)
59332     {
59333         var view = this.grid.view ? this.grid.view : this.grid;
59334         var rowIndex;
59335         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59336             return;
59337         };
59338         if(e.shiftKey && this.last !== false){
59339             var last = this.last;
59340             this.selectRange(last, rowIndex, e.ctrlKey);
59341             this.last = last; // reset the last
59342             view.focusRow(rowIndex);
59343         }else{
59344             var isSelected = this.isSelected(rowIndex);
59345             if(e.button !== 0 && isSelected){
59346                 view.focusRow(rowIndex);
59347             }else if(e.ctrlKey && isSelected){
59348                 this.deselectRow(rowIndex);
59349             }else if(!isSelected){
59350                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59351                 view.focusRow(rowIndex);
59352             }
59353         }
59354         this.fireEvent("afterselectionchange", this);
59355     },
59356     // private
59357     handleDragableRowClick :  function(grid, rowIndex, e) 
59358     {
59359         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59360             this.selectRow(rowIndex, false);
59361             var view = this.grid.view ? this.grid.view : this.grid;
59362             view.focusRow(rowIndex);
59363              this.fireEvent("afterselectionchange", this);
59364         }
59365     },
59366     
59367     /**
59368      * Selects multiple rows.
59369      * @param {Array} rows Array of the indexes of the row to select
59370      * @param {Boolean} keepExisting (optional) True to keep existing selections
59371      */
59372     selectRows : function(rows, keepExisting){
59373         if(!keepExisting){
59374             this.clearSelections();
59375         }
59376         for(var i = 0, len = rows.length; i < len; i++){
59377             this.selectRow(rows[i], true);
59378         }
59379     },
59380
59381     /**
59382      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59383      * @param {Number} startRow The index of the first row in the range
59384      * @param {Number} endRow The index of the last row in the range
59385      * @param {Boolean} keepExisting (optional) True to retain existing selections
59386      */
59387     selectRange : function(startRow, endRow, keepExisting){
59388         if(this.locked) {
59389             return;
59390         }
59391         if(!keepExisting){
59392             this.clearSelections();
59393         }
59394         if(startRow <= endRow){
59395             for(var i = startRow; i <= endRow; i++){
59396                 this.selectRow(i, true);
59397             }
59398         }else{
59399             for(var i = startRow; i >= endRow; i--){
59400                 this.selectRow(i, true);
59401             }
59402         }
59403     },
59404
59405     /**
59406      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59407      * @param {Number} startRow The index of the first row in the range
59408      * @param {Number} endRow The index of the last row in the range
59409      */
59410     deselectRange : function(startRow, endRow, preventViewNotify){
59411         if(this.locked) {
59412             return;
59413         }
59414         for(var i = startRow; i <= endRow; i++){
59415             this.deselectRow(i, preventViewNotify);
59416         }
59417     },
59418
59419     /**
59420      * Selects a row.
59421      * @param {Number} row The index of the row to select
59422      * @param {Boolean} keepExisting (optional) True to keep existing selections
59423      */
59424     selectRow : function(index, keepExisting, preventViewNotify){
59425         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59426             return;
59427         }
59428         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59429             if(!keepExisting || this.singleSelect){
59430                 this.clearSelections();
59431             }
59432             var r = this.grid.ds.getAt(index);
59433             this.selections.add(r);
59434             this.last = this.lastActive = index;
59435             if(!preventViewNotify){
59436                 var view = this.grid.view ? this.grid.view : this.grid;
59437                 view.onRowSelect(index);
59438             }
59439             this.fireEvent("rowselect", this, index, r);
59440             this.fireEvent("selectionchange", this);
59441         }
59442     },
59443
59444     /**
59445      * Deselects a row.
59446      * @param {Number} row The index of the row to deselect
59447      */
59448     deselectRow : function(index, preventViewNotify){
59449         if(this.locked) {
59450             return;
59451         }
59452         if(this.last == index){
59453             this.last = false;
59454         }
59455         if(this.lastActive == index){
59456             this.lastActive = false;
59457         }
59458         var r = this.grid.ds.getAt(index);
59459         this.selections.remove(r);
59460         if(!preventViewNotify){
59461             var view = this.grid.view ? this.grid.view : this.grid;
59462             view.onRowDeselect(index);
59463         }
59464         this.fireEvent("rowdeselect", this, index);
59465         this.fireEvent("selectionchange", this);
59466     },
59467
59468     // private
59469     restoreLast : function(){
59470         if(this._last){
59471             this.last = this._last;
59472         }
59473     },
59474
59475     // private
59476     acceptsNav : function(row, col, cm){
59477         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59478     },
59479
59480     // private
59481     onEditorKey : function(field, e){
59482         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59483         if(k == e.TAB){
59484             e.stopEvent();
59485             ed.completeEdit();
59486             if(e.shiftKey){
59487                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59488             }else{
59489                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59490             }
59491         }else if(k == e.ENTER && !e.ctrlKey){
59492             e.stopEvent();
59493             ed.completeEdit();
59494             if(e.shiftKey){
59495                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59496             }else{
59497                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59498             }
59499         }else if(k == e.ESC){
59500             ed.cancelEdit();
59501         }
59502         if(newCell){
59503             g.startEditing(newCell[0], newCell[1]);
59504         }
59505     }
59506 });/*
59507  * Based on:
59508  * Ext JS Library 1.1.1
59509  * Copyright(c) 2006-2007, Ext JS, LLC.
59510  *
59511  * Originally Released Under LGPL - original licence link has changed is not relivant.
59512  *
59513  * Fork - LGPL
59514  * <script type="text/javascript">
59515  */
59516 /**
59517  * @class Roo.grid.CellSelectionModel
59518  * @extends Roo.grid.AbstractSelectionModel
59519  * This class provides the basic implementation for cell selection in a grid.
59520  * @constructor
59521  * @param {Object} config The object containing the configuration of this model.
59522  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59523  */
59524 Roo.grid.CellSelectionModel = function(config){
59525     Roo.apply(this, config);
59526
59527     this.selection = null;
59528
59529     this.addEvents({
59530         /**
59531              * @event beforerowselect
59532              * Fires before a cell is selected.
59533              * @param {SelectionModel} this
59534              * @param {Number} rowIndex The selected row index
59535              * @param {Number} colIndex The selected cell index
59536              */
59537             "beforecellselect" : true,
59538         /**
59539              * @event cellselect
59540              * Fires when a cell is selected.
59541              * @param {SelectionModel} this
59542              * @param {Number} rowIndex The selected row index
59543              * @param {Number} colIndex The selected cell index
59544              */
59545             "cellselect" : true,
59546         /**
59547              * @event selectionchange
59548              * Fires when the active selection changes.
59549              * @param {SelectionModel} this
59550              * @param {Object} selection null for no selection or an object (o) with two properties
59551                 <ul>
59552                 <li>o.record: the record object for the row the selection is in</li>
59553                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59554                 </ul>
59555              */
59556             "selectionchange" : true,
59557         /**
59558              * @event tabend
59559              * Fires when the tab (or enter) was pressed on the last editable cell
59560              * You can use this to trigger add new row.
59561              * @param {SelectionModel} this
59562              */
59563             "tabend" : true,
59564          /**
59565              * @event beforeeditnext
59566              * Fires before the next editable sell is made active
59567              * You can use this to skip to another cell or fire the tabend
59568              *    if you set cell to false
59569              * @param {Object} eventdata object : { cell : [ row, col ] } 
59570              */
59571             "beforeeditnext" : true
59572     });
59573     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59574 };
59575
59576 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59577     
59578     enter_is_tab: false,
59579
59580     /** @ignore */
59581     initEvents : function(){
59582         this.grid.on("mousedown", this.handleMouseDown, this);
59583         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59584         var view = this.grid.view;
59585         view.on("refresh", this.onViewChange, this);
59586         view.on("rowupdated", this.onRowUpdated, this);
59587         view.on("beforerowremoved", this.clearSelections, this);
59588         view.on("beforerowsinserted", this.clearSelections, this);
59589         if(this.grid.isEditor){
59590             this.grid.on("beforeedit", this.beforeEdit,  this);
59591         }
59592     },
59593
59594         //private
59595     beforeEdit : function(e){
59596         this.select(e.row, e.column, false, true, e.record);
59597     },
59598
59599         //private
59600     onRowUpdated : function(v, index, r){
59601         if(this.selection && this.selection.record == r){
59602             v.onCellSelect(index, this.selection.cell[1]);
59603         }
59604     },
59605
59606         //private
59607     onViewChange : function(){
59608         this.clearSelections(true);
59609     },
59610
59611         /**
59612          * Returns the currently selected cell,.
59613          * @return {Array} The selected cell (row, column) or null if none selected.
59614          */
59615     getSelectedCell : function(){
59616         return this.selection ? this.selection.cell : null;
59617     },
59618
59619     /**
59620      * Clears all selections.
59621      * @param {Boolean} true to prevent the gridview from being notified about the change.
59622      */
59623     clearSelections : function(preventNotify){
59624         var s = this.selection;
59625         if(s){
59626             if(preventNotify !== true){
59627                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59628             }
59629             this.selection = null;
59630             this.fireEvent("selectionchange", this, null);
59631         }
59632     },
59633
59634     /**
59635      * Returns true if there is a selection.
59636      * @return {Boolean}
59637      */
59638     hasSelection : function(){
59639         return this.selection ? true : false;
59640     },
59641
59642     /** @ignore */
59643     handleMouseDown : function(e, t){
59644         var v = this.grid.getView();
59645         if(this.isLocked()){
59646             return;
59647         };
59648         var row = v.findRowIndex(t);
59649         var cell = v.findCellIndex(t);
59650         if(row !== false && cell !== false){
59651             this.select(row, cell);
59652         }
59653     },
59654
59655     /**
59656      * Selects a cell.
59657      * @param {Number} rowIndex
59658      * @param {Number} collIndex
59659      */
59660     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59661         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59662             this.clearSelections();
59663             r = r || this.grid.dataSource.getAt(rowIndex);
59664             this.selection = {
59665                 record : r,
59666                 cell : [rowIndex, colIndex]
59667             };
59668             if(!preventViewNotify){
59669                 var v = this.grid.getView();
59670                 v.onCellSelect(rowIndex, colIndex);
59671                 if(preventFocus !== true){
59672                     v.focusCell(rowIndex, colIndex);
59673                 }
59674             }
59675             this.fireEvent("cellselect", this, rowIndex, colIndex);
59676             this.fireEvent("selectionchange", this, this.selection);
59677         }
59678     },
59679
59680         //private
59681     isSelectable : function(rowIndex, colIndex, cm){
59682         return !cm.isHidden(colIndex);
59683     },
59684
59685     /** @ignore */
59686     handleKeyDown : function(e){
59687         //Roo.log('Cell Sel Model handleKeyDown');
59688         if(!e.isNavKeyPress()){
59689             return;
59690         }
59691         var g = this.grid, s = this.selection;
59692         if(!s){
59693             e.stopEvent();
59694             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59695             if(cell){
59696                 this.select(cell[0], cell[1]);
59697             }
59698             return;
59699         }
59700         var sm = this;
59701         var walk = function(row, col, step){
59702             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59703         };
59704         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59705         var newCell;
59706
59707       
59708
59709         switch(k){
59710             case e.TAB:
59711                 // handled by onEditorKey
59712                 if (g.isEditor && g.editing) {
59713                     return;
59714                 }
59715                 if(e.shiftKey) {
59716                     newCell = walk(r, c-1, -1);
59717                 } else {
59718                     newCell = walk(r, c+1, 1);
59719                 }
59720                 break;
59721             
59722             case e.DOWN:
59723                newCell = walk(r+1, c, 1);
59724                 break;
59725             
59726             case e.UP:
59727                 newCell = walk(r-1, c, -1);
59728                 break;
59729             
59730             case e.RIGHT:
59731                 newCell = walk(r, c+1, 1);
59732                 break;
59733             
59734             case e.LEFT:
59735                 newCell = walk(r, c-1, -1);
59736                 break;
59737             
59738             case e.ENTER:
59739                 
59740                 if(g.isEditor && !g.editing){
59741                    g.startEditing(r, c);
59742                    e.stopEvent();
59743                    return;
59744                 }
59745                 
59746                 
59747              break;
59748         };
59749         if(newCell){
59750             this.select(newCell[0], newCell[1]);
59751             e.stopEvent();
59752             
59753         }
59754     },
59755
59756     acceptsNav : function(row, col, cm){
59757         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59758     },
59759     /**
59760      * Selects a cell.
59761      * @param {Number} field (not used) - as it's normally used as a listener
59762      * @param {Number} e - event - fake it by using
59763      *
59764      * var e = Roo.EventObjectImpl.prototype;
59765      * e.keyCode = e.TAB
59766      *
59767      * 
59768      */
59769     onEditorKey : function(field, e){
59770         
59771         var k = e.getKey(),
59772             newCell,
59773             g = this.grid,
59774             ed = g.activeEditor,
59775             forward = false;
59776         ///Roo.log('onEditorKey' + k);
59777         
59778         
59779         if (this.enter_is_tab && k == e.ENTER) {
59780             k = e.TAB;
59781         }
59782         
59783         if(k == e.TAB){
59784             if(e.shiftKey){
59785                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59786             }else{
59787                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59788                 forward = true;
59789             }
59790             
59791             e.stopEvent();
59792             
59793         } else if(k == e.ENTER &&  !e.ctrlKey){
59794             ed.completeEdit();
59795             e.stopEvent();
59796             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59797         
59798                 } else if(k == e.ESC){
59799             ed.cancelEdit();
59800         }
59801                 
59802         if (newCell) {
59803             var ecall = { cell : newCell, forward : forward };
59804             this.fireEvent('beforeeditnext', ecall );
59805             newCell = ecall.cell;
59806                         forward = ecall.forward;
59807         }
59808                 
59809         if(newCell){
59810             //Roo.log('next cell after edit');
59811             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59812         } else if (forward) {
59813             // tabbed past last
59814             this.fireEvent.defer(100, this, ['tabend',this]);
59815         }
59816     }
59817 });/*
59818  * Based on:
59819  * Ext JS Library 1.1.1
59820  * Copyright(c) 2006-2007, Ext JS, LLC.
59821  *
59822  * Originally Released Under LGPL - original licence link has changed is not relivant.
59823  *
59824  * Fork - LGPL
59825  * <script type="text/javascript">
59826  */
59827  
59828 /**
59829  * @class Roo.grid.EditorGrid
59830  * @extends Roo.grid.Grid
59831  * Class for creating and editable grid.
59832  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59833  * The container MUST have some type of size defined for the grid to fill. The container will be 
59834  * automatically set to position relative if it isn't already.
59835  * @param {Object} dataSource The data model to bind to
59836  * @param {Object} colModel The column model with info about this grid's columns
59837  */
59838 Roo.grid.EditorGrid = function(container, config){
59839     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59840     this.getGridEl().addClass("xedit-grid");
59841
59842     if(!this.selModel){
59843         this.selModel = new Roo.grid.CellSelectionModel();
59844     }
59845
59846     this.activeEditor = null;
59847
59848         this.addEvents({
59849             /**
59850              * @event beforeedit
59851              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59852              * <ul style="padding:5px;padding-left:16px;">
59853              * <li>grid - This grid</li>
59854              * <li>record - The record being edited</li>
59855              * <li>field - The field name being edited</li>
59856              * <li>value - The value for the field being edited.</li>
59857              * <li>row - The grid row index</li>
59858              * <li>column - The grid column index</li>
59859              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59860              * </ul>
59861              * @param {Object} e An edit event (see above for description)
59862              */
59863             "beforeedit" : true,
59864             /**
59865              * @event afteredit
59866              * Fires after a cell is edited. <br />
59867              * <ul style="padding:5px;padding-left:16px;">
59868              * <li>grid - This grid</li>
59869              * <li>record - The record being edited</li>
59870              * <li>field - The field name being edited</li>
59871              * <li>value - The value being set</li>
59872              * <li>originalValue - The original value for the field, before the edit.</li>
59873              * <li>row - The grid row index</li>
59874              * <li>column - The grid column index</li>
59875              * </ul>
59876              * @param {Object} e An edit event (see above for description)
59877              */
59878             "afteredit" : true,
59879             /**
59880              * @event validateedit
59881              * Fires after a cell is edited, but before the value is set in the record. 
59882          * You can use this to modify the value being set in the field, Return false
59883              * to cancel the change. The edit event object has the following properties <br />
59884              * <ul style="padding:5px;padding-left:16px;">
59885          * <li>editor - This editor</li>
59886              * <li>grid - This grid</li>
59887              * <li>record - The record being edited</li>
59888              * <li>field - The field name being edited</li>
59889              * <li>value - The value being set</li>
59890              * <li>originalValue - The original value for the field, before the edit.</li>
59891              * <li>row - The grid row index</li>
59892              * <li>column - The grid column index</li>
59893              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59894              * </ul>
59895              * @param {Object} e An edit event (see above for description)
59896              */
59897             "validateedit" : true
59898         });
59899     this.on("bodyscroll", this.stopEditing,  this);
59900     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59901 };
59902
59903 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59904     /**
59905      * @cfg {Number} clicksToEdit
59906      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59907      */
59908     clicksToEdit: 2,
59909
59910     // private
59911     isEditor : true,
59912     // private
59913     trackMouseOver: false, // causes very odd FF errors
59914
59915     onCellDblClick : function(g, row, col){
59916         this.startEditing(row, col);
59917     },
59918
59919     onEditComplete : function(ed, value, startValue){
59920         this.editing = false;
59921         this.activeEditor = null;
59922         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59923         var r = ed.record;
59924         var field = this.colModel.getDataIndex(ed.col);
59925         var e = {
59926             grid: this,
59927             record: r,
59928             field: field,
59929             originalValue: startValue,
59930             value: value,
59931             row: ed.row,
59932             column: ed.col,
59933             cancel:false,
59934             editor: ed
59935         };
59936         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59937         cell.show();
59938           
59939         if(String(value) !== String(startValue)){
59940             
59941             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59942                 r.set(field, e.value);
59943                 // if we are dealing with a combo box..
59944                 // then we also set the 'name' colum to be the displayField
59945                 if (ed.field.displayField && ed.field.name) {
59946                     r.set(ed.field.name, ed.field.el.dom.value);
59947                 }
59948                 
59949                 delete e.cancel; //?? why!!!
59950                 this.fireEvent("afteredit", e);
59951             }
59952         } else {
59953             this.fireEvent("afteredit", e); // always fire it!
59954         }
59955         this.view.focusCell(ed.row, ed.col);
59956     },
59957
59958     /**
59959      * Starts editing the specified for the specified row/column
59960      * @param {Number} rowIndex
59961      * @param {Number} colIndex
59962      */
59963     startEditing : function(row, col){
59964         this.stopEditing();
59965         if(this.colModel.isCellEditable(col, row)){
59966             this.view.ensureVisible(row, col, true);
59967           
59968             var r = this.dataSource.getAt(row);
59969             var field = this.colModel.getDataIndex(col);
59970             var cell = Roo.get(this.view.getCell(row,col));
59971             var e = {
59972                 grid: this,
59973                 record: r,
59974                 field: field,
59975                 value: r.data[field],
59976                 row: row,
59977                 column: col,
59978                 cancel:false 
59979             };
59980             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59981                 this.editing = true;
59982                 var ed = this.colModel.getCellEditor(col, row);
59983                 
59984                 if (!ed) {
59985                     return;
59986                 }
59987                 if(!ed.rendered){
59988                     ed.render(ed.parentEl || document.body);
59989                 }
59990                 ed.field.reset();
59991                
59992                 cell.hide();
59993                 
59994                 (function(){ // complex but required for focus issues in safari, ie and opera
59995                     ed.row = row;
59996                     ed.col = col;
59997                     ed.record = r;
59998                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59999                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60000                     this.activeEditor = ed;
60001                     var v = r.data[field];
60002                     ed.startEdit(this.view.getCell(row, col), v);
60003                     // combo's with 'displayField and name set
60004                     if (ed.field.displayField && ed.field.name) {
60005                         ed.field.el.dom.value = r.data[ed.field.name];
60006                     }
60007                     
60008                     
60009                 }).defer(50, this);
60010             }
60011         }
60012     },
60013         
60014     /**
60015      * Stops any active editing
60016      */
60017     stopEditing : function(){
60018         if(this.activeEditor){
60019             this.activeEditor.completeEdit();
60020         }
60021         this.activeEditor = null;
60022     },
60023         
60024          /**
60025      * Called to get grid's drag proxy text, by default returns this.ddText.
60026      * @return {String}
60027      */
60028     getDragDropText : function(){
60029         var count = this.selModel.getSelectedCell() ? 1 : 0;
60030         return String.format(this.ddText, count, count == 1 ? '' : 's');
60031     }
60032         
60033 });/*
60034  * Based on:
60035  * Ext JS Library 1.1.1
60036  * Copyright(c) 2006-2007, Ext JS, LLC.
60037  *
60038  * Originally Released Under LGPL - original licence link has changed is not relivant.
60039  *
60040  * Fork - LGPL
60041  * <script type="text/javascript">
60042  */
60043
60044 // private - not really -- you end up using it !
60045 // This is a support class used internally by the Grid components
60046
60047 /**
60048  * @class Roo.grid.GridEditor
60049  * @extends Roo.Editor
60050  * Class for creating and editable grid elements.
60051  * @param {Object} config any settings (must include field)
60052  */
60053 Roo.grid.GridEditor = function(field, config){
60054     if (!config && field.field) {
60055         config = field;
60056         field = Roo.factory(config.field, Roo.form);
60057     }
60058     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60059     field.monitorTab = false;
60060 };
60061
60062 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60063     
60064     /**
60065      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60066      */
60067     
60068     alignment: "tl-tl",
60069     autoSize: "width",
60070     hideEl : false,
60071     cls: "x-small-editor x-grid-editor",
60072     shim:false,
60073     shadow:"frame"
60074 });/*
60075  * Based on:
60076  * Ext JS Library 1.1.1
60077  * Copyright(c) 2006-2007, Ext JS, LLC.
60078  *
60079  * Originally Released Under LGPL - original licence link has changed is not relivant.
60080  *
60081  * Fork - LGPL
60082  * <script type="text/javascript">
60083  */
60084   
60085
60086   
60087 Roo.grid.PropertyRecord = Roo.data.Record.create([
60088     {name:'name',type:'string'},  'value'
60089 ]);
60090
60091
60092 Roo.grid.PropertyStore = function(grid, source){
60093     this.grid = grid;
60094     this.store = new Roo.data.Store({
60095         recordType : Roo.grid.PropertyRecord
60096     });
60097     this.store.on('update', this.onUpdate,  this);
60098     if(source){
60099         this.setSource(source);
60100     }
60101     Roo.grid.PropertyStore.superclass.constructor.call(this);
60102 };
60103
60104
60105
60106 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60107     setSource : function(o){
60108         this.source = o;
60109         this.store.removeAll();
60110         var data = [];
60111         for(var k in o){
60112             if(this.isEditableValue(o[k])){
60113                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60114             }
60115         }
60116         this.store.loadRecords({records: data}, {}, true);
60117     },
60118
60119     onUpdate : function(ds, record, type){
60120         if(type == Roo.data.Record.EDIT){
60121             var v = record.data['value'];
60122             var oldValue = record.modified['value'];
60123             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60124                 this.source[record.id] = v;
60125                 record.commit();
60126                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60127             }else{
60128                 record.reject();
60129             }
60130         }
60131     },
60132
60133     getProperty : function(row){
60134        return this.store.getAt(row);
60135     },
60136
60137     isEditableValue: function(val){
60138         if(val && val instanceof Date){
60139             return true;
60140         }else if(typeof val == 'object' || typeof val == 'function'){
60141             return false;
60142         }
60143         return true;
60144     },
60145
60146     setValue : function(prop, value){
60147         this.source[prop] = value;
60148         this.store.getById(prop).set('value', value);
60149     },
60150
60151     getSource : function(){
60152         return this.source;
60153     }
60154 });
60155
60156 Roo.grid.PropertyColumnModel = function(grid, store){
60157     this.grid = grid;
60158     var g = Roo.grid;
60159     g.PropertyColumnModel.superclass.constructor.call(this, [
60160         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60161         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60162     ]);
60163     this.store = store;
60164     this.bselect = Roo.DomHelper.append(document.body, {
60165         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60166             {tag: 'option', value: 'true', html: 'true'},
60167             {tag: 'option', value: 'false', html: 'false'}
60168         ]
60169     });
60170     Roo.id(this.bselect);
60171     var f = Roo.form;
60172     this.editors = {
60173         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60174         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60175         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60176         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60177         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60178     };
60179     this.renderCellDelegate = this.renderCell.createDelegate(this);
60180     this.renderPropDelegate = this.renderProp.createDelegate(this);
60181 };
60182
60183 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60184     
60185     
60186     nameText : 'Name',
60187     valueText : 'Value',
60188     
60189     dateFormat : 'm/j/Y',
60190     
60191     
60192     renderDate : function(dateVal){
60193         return dateVal.dateFormat(this.dateFormat);
60194     },
60195
60196     renderBool : function(bVal){
60197         return bVal ? 'true' : 'false';
60198     },
60199
60200     isCellEditable : function(colIndex, rowIndex){
60201         return colIndex == 1;
60202     },
60203
60204     getRenderer : function(col){
60205         return col == 1 ?
60206             this.renderCellDelegate : this.renderPropDelegate;
60207     },
60208
60209     renderProp : function(v){
60210         return this.getPropertyName(v);
60211     },
60212
60213     renderCell : function(val){
60214         var rv = val;
60215         if(val instanceof Date){
60216             rv = this.renderDate(val);
60217         }else if(typeof val == 'boolean'){
60218             rv = this.renderBool(val);
60219         }
60220         return Roo.util.Format.htmlEncode(rv);
60221     },
60222
60223     getPropertyName : function(name){
60224         var pn = this.grid.propertyNames;
60225         return pn && pn[name] ? pn[name] : name;
60226     },
60227
60228     getCellEditor : function(colIndex, rowIndex){
60229         var p = this.store.getProperty(rowIndex);
60230         var n = p.data['name'], val = p.data['value'];
60231         
60232         if(typeof(this.grid.customEditors[n]) == 'string'){
60233             return this.editors[this.grid.customEditors[n]];
60234         }
60235         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60236             return this.grid.customEditors[n];
60237         }
60238         if(val instanceof Date){
60239             return this.editors['date'];
60240         }else if(typeof val == 'number'){
60241             return this.editors['number'];
60242         }else if(typeof val == 'boolean'){
60243             return this.editors['boolean'];
60244         }else{
60245             return this.editors['string'];
60246         }
60247     }
60248 });
60249
60250 /**
60251  * @class Roo.grid.PropertyGrid
60252  * @extends Roo.grid.EditorGrid
60253  * This class represents the  interface of a component based property grid control.
60254  * <br><br>Usage:<pre><code>
60255  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60256       
60257  });
60258  // set any options
60259  grid.render();
60260  * </code></pre>
60261   
60262  * @constructor
60263  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60264  * The container MUST have some type of size defined for the grid to fill. The container will be
60265  * automatically set to position relative if it isn't already.
60266  * @param {Object} config A config object that sets properties on this grid.
60267  */
60268 Roo.grid.PropertyGrid = function(container, config){
60269     config = config || {};
60270     var store = new Roo.grid.PropertyStore(this);
60271     this.store = store;
60272     var cm = new Roo.grid.PropertyColumnModel(this, store);
60273     store.store.sort('name', 'ASC');
60274     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60275         ds: store.store,
60276         cm: cm,
60277         enableColLock:false,
60278         enableColumnMove:false,
60279         stripeRows:false,
60280         trackMouseOver: false,
60281         clicksToEdit:1
60282     }, config));
60283     this.getGridEl().addClass('x-props-grid');
60284     this.lastEditRow = null;
60285     this.on('columnresize', this.onColumnResize, this);
60286     this.addEvents({
60287          /**
60288              * @event beforepropertychange
60289              * Fires before a property changes (return false to stop?)
60290              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60291              * @param {String} id Record Id
60292              * @param {String} newval New Value
60293          * @param {String} oldval Old Value
60294              */
60295         "beforepropertychange": true,
60296         /**
60297              * @event propertychange
60298              * Fires after a property changes
60299              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60300              * @param {String} id Record Id
60301              * @param {String} newval New Value
60302          * @param {String} oldval Old Value
60303              */
60304         "propertychange": true
60305     });
60306     this.customEditors = this.customEditors || {};
60307 };
60308 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60309     
60310      /**
60311      * @cfg {Object} customEditors map of colnames=> custom editors.
60312      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60313      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60314      * false disables editing of the field.
60315          */
60316     
60317       /**
60318      * @cfg {Object} propertyNames map of property Names to their displayed value
60319          */
60320     
60321     render : function(){
60322         Roo.grid.PropertyGrid.superclass.render.call(this);
60323         this.autoSize.defer(100, this);
60324     },
60325
60326     autoSize : function(){
60327         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60328         if(this.view){
60329             this.view.fitColumns();
60330         }
60331     },
60332
60333     onColumnResize : function(){
60334         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60335         this.autoSize();
60336     },
60337     /**
60338      * Sets the data for the Grid
60339      * accepts a Key => Value object of all the elements avaiable.
60340      * @param {Object} data  to appear in grid.
60341      */
60342     setSource : function(source){
60343         this.store.setSource(source);
60344         //this.autoSize();
60345     },
60346     /**
60347      * Gets all the data from the grid.
60348      * @return {Object} data  data stored in grid
60349      */
60350     getSource : function(){
60351         return this.store.getSource();
60352     }
60353 });/*
60354   
60355  * Licence LGPL
60356  
60357  */
60358  
60359 /**
60360  * @class Roo.grid.Calendar
60361  * @extends Roo.util.Grid
60362  * This class extends the Grid to provide a calendar widget
60363  * <br><br>Usage:<pre><code>
60364  var grid = new Roo.grid.Calendar("my-container-id", {
60365      ds: myDataStore,
60366      cm: myColModel,
60367      selModel: mySelectionModel,
60368      autoSizeColumns: true,
60369      monitorWindowResize: false,
60370      trackMouseOver: true
60371      eventstore : real data store..
60372  });
60373  // set any options
60374  grid.render();
60375   
60376   * @constructor
60377  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60378  * The container MUST have some type of size defined for the grid to fill. The container will be
60379  * automatically set to position relative if it isn't already.
60380  * @param {Object} config A config object that sets properties on this grid.
60381  */
60382 Roo.grid.Calendar = function(container, config){
60383         // initialize the container
60384         this.container = Roo.get(container);
60385         this.container.update("");
60386         this.container.setStyle("overflow", "hidden");
60387     this.container.addClass('x-grid-container');
60388
60389     this.id = this.container.id;
60390
60391     Roo.apply(this, config);
60392     // check and correct shorthanded configs
60393     
60394     var rows = [];
60395     var d =1;
60396     for (var r = 0;r < 6;r++) {
60397         
60398         rows[r]=[];
60399         for (var c =0;c < 7;c++) {
60400             rows[r][c]= '';
60401         }
60402     }
60403     if (this.eventStore) {
60404         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60405         this.eventStore.on('load',this.onLoad, this);
60406         this.eventStore.on('beforeload',this.clearEvents, this);
60407          
60408     }
60409     
60410     this.dataSource = new Roo.data.Store({
60411             proxy: new Roo.data.MemoryProxy(rows),
60412             reader: new Roo.data.ArrayReader({}, [
60413                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60414     });
60415
60416     this.dataSource.load();
60417     this.ds = this.dataSource;
60418     this.ds.xmodule = this.xmodule || false;
60419     
60420     
60421     var cellRender = function(v,x,r)
60422     {
60423         return String.format(
60424             '<div class="fc-day  fc-widget-content"><div>' +
60425                 '<div class="fc-event-container"></div>' +
60426                 '<div class="fc-day-number">{0}</div>'+
60427                 
60428                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60429             '</div></div>', v);
60430     
60431     }
60432     
60433     
60434     this.colModel = new Roo.grid.ColumnModel( [
60435         {
60436             xtype: 'ColumnModel',
60437             xns: Roo.grid,
60438             dataIndex : 'weekday0',
60439             header : 'Sunday',
60440             renderer : cellRender
60441         },
60442         {
60443             xtype: 'ColumnModel',
60444             xns: Roo.grid,
60445             dataIndex : 'weekday1',
60446             header : 'Monday',
60447             renderer : cellRender
60448         },
60449         {
60450             xtype: 'ColumnModel',
60451             xns: Roo.grid,
60452             dataIndex : 'weekday2',
60453             header : 'Tuesday',
60454             renderer : cellRender
60455         },
60456         {
60457             xtype: 'ColumnModel',
60458             xns: Roo.grid,
60459             dataIndex : 'weekday3',
60460             header : 'Wednesday',
60461             renderer : cellRender
60462         },
60463         {
60464             xtype: 'ColumnModel',
60465             xns: Roo.grid,
60466             dataIndex : 'weekday4',
60467             header : 'Thursday',
60468             renderer : cellRender
60469         },
60470         {
60471             xtype: 'ColumnModel',
60472             xns: Roo.grid,
60473             dataIndex : 'weekday5',
60474             header : 'Friday',
60475             renderer : cellRender
60476         },
60477         {
60478             xtype: 'ColumnModel',
60479             xns: Roo.grid,
60480             dataIndex : 'weekday6',
60481             header : 'Saturday',
60482             renderer : cellRender
60483         }
60484     ]);
60485     this.cm = this.colModel;
60486     this.cm.xmodule = this.xmodule || false;
60487  
60488         
60489           
60490     //this.selModel = new Roo.grid.CellSelectionModel();
60491     //this.sm = this.selModel;
60492     //this.selModel.init(this);
60493     
60494     
60495     if(this.width){
60496         this.container.setWidth(this.width);
60497     }
60498
60499     if(this.height){
60500         this.container.setHeight(this.height);
60501     }
60502     /** @private */
60503         this.addEvents({
60504         // raw events
60505         /**
60506          * @event click
60507          * The raw click event for the entire grid.
60508          * @param {Roo.EventObject} e
60509          */
60510         "click" : true,
60511         /**
60512          * @event dblclick
60513          * The raw dblclick event for the entire grid.
60514          * @param {Roo.EventObject} e
60515          */
60516         "dblclick" : true,
60517         /**
60518          * @event contextmenu
60519          * The raw contextmenu event for the entire grid.
60520          * @param {Roo.EventObject} e
60521          */
60522         "contextmenu" : true,
60523         /**
60524          * @event mousedown
60525          * The raw mousedown event for the entire grid.
60526          * @param {Roo.EventObject} e
60527          */
60528         "mousedown" : true,
60529         /**
60530          * @event mouseup
60531          * The raw mouseup event for the entire grid.
60532          * @param {Roo.EventObject} e
60533          */
60534         "mouseup" : true,
60535         /**
60536          * @event mouseover
60537          * The raw mouseover event for the entire grid.
60538          * @param {Roo.EventObject} e
60539          */
60540         "mouseover" : true,
60541         /**
60542          * @event mouseout
60543          * The raw mouseout event for the entire grid.
60544          * @param {Roo.EventObject} e
60545          */
60546         "mouseout" : true,
60547         /**
60548          * @event keypress
60549          * The raw keypress event for the entire grid.
60550          * @param {Roo.EventObject} e
60551          */
60552         "keypress" : true,
60553         /**
60554          * @event keydown
60555          * The raw keydown event for the entire grid.
60556          * @param {Roo.EventObject} e
60557          */
60558         "keydown" : true,
60559
60560         // custom events
60561
60562         /**
60563          * @event cellclick
60564          * Fires when a cell is clicked
60565          * @param {Grid} this
60566          * @param {Number} rowIndex
60567          * @param {Number} columnIndex
60568          * @param {Roo.EventObject} e
60569          */
60570         "cellclick" : true,
60571         /**
60572          * @event celldblclick
60573          * Fires when a cell is double clicked
60574          * @param {Grid} this
60575          * @param {Number} rowIndex
60576          * @param {Number} columnIndex
60577          * @param {Roo.EventObject} e
60578          */
60579         "celldblclick" : true,
60580         /**
60581          * @event rowclick
60582          * Fires when a row is clicked
60583          * @param {Grid} this
60584          * @param {Number} rowIndex
60585          * @param {Roo.EventObject} e
60586          */
60587         "rowclick" : true,
60588         /**
60589          * @event rowdblclick
60590          * Fires when a row is double clicked
60591          * @param {Grid} this
60592          * @param {Number} rowIndex
60593          * @param {Roo.EventObject} e
60594          */
60595         "rowdblclick" : true,
60596         /**
60597          * @event headerclick
60598          * Fires when a header is clicked
60599          * @param {Grid} this
60600          * @param {Number} columnIndex
60601          * @param {Roo.EventObject} e
60602          */
60603         "headerclick" : true,
60604         /**
60605          * @event headerdblclick
60606          * Fires when a header cell is double clicked
60607          * @param {Grid} this
60608          * @param {Number} columnIndex
60609          * @param {Roo.EventObject} e
60610          */
60611         "headerdblclick" : true,
60612         /**
60613          * @event rowcontextmenu
60614          * Fires when a row is right clicked
60615          * @param {Grid} this
60616          * @param {Number} rowIndex
60617          * @param {Roo.EventObject} e
60618          */
60619         "rowcontextmenu" : true,
60620         /**
60621          * @event cellcontextmenu
60622          * Fires when a cell is right clicked
60623          * @param {Grid} this
60624          * @param {Number} rowIndex
60625          * @param {Number} cellIndex
60626          * @param {Roo.EventObject} e
60627          */
60628          "cellcontextmenu" : true,
60629         /**
60630          * @event headercontextmenu
60631          * Fires when a header is right clicked
60632          * @param {Grid} this
60633          * @param {Number} columnIndex
60634          * @param {Roo.EventObject} e
60635          */
60636         "headercontextmenu" : true,
60637         /**
60638          * @event bodyscroll
60639          * Fires when the body element is scrolled
60640          * @param {Number} scrollLeft
60641          * @param {Number} scrollTop
60642          */
60643         "bodyscroll" : true,
60644         /**
60645          * @event columnresize
60646          * Fires when the user resizes a column
60647          * @param {Number} columnIndex
60648          * @param {Number} newSize
60649          */
60650         "columnresize" : true,
60651         /**
60652          * @event columnmove
60653          * Fires when the user moves a column
60654          * @param {Number} oldIndex
60655          * @param {Number} newIndex
60656          */
60657         "columnmove" : true,
60658         /**
60659          * @event startdrag
60660          * Fires when row(s) start being dragged
60661          * @param {Grid} this
60662          * @param {Roo.GridDD} dd The drag drop object
60663          * @param {event} e The raw browser event
60664          */
60665         "startdrag" : true,
60666         /**
60667          * @event enddrag
60668          * Fires when a drag operation is complete
60669          * @param {Grid} this
60670          * @param {Roo.GridDD} dd The drag drop object
60671          * @param {event} e The raw browser event
60672          */
60673         "enddrag" : true,
60674         /**
60675          * @event dragdrop
60676          * Fires when dragged row(s) are dropped on a valid DD target
60677          * @param {Grid} this
60678          * @param {Roo.GridDD} dd The drag drop object
60679          * @param {String} targetId The target drag drop object
60680          * @param {event} e The raw browser event
60681          */
60682         "dragdrop" : true,
60683         /**
60684          * @event dragover
60685          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60686          * @param {Grid} this
60687          * @param {Roo.GridDD} dd The drag drop object
60688          * @param {String} targetId The target drag drop object
60689          * @param {event} e The raw browser event
60690          */
60691         "dragover" : true,
60692         /**
60693          * @event dragenter
60694          *  Fires when the dragged row(s) first cross another DD target while being dragged
60695          * @param {Grid} this
60696          * @param {Roo.GridDD} dd The drag drop object
60697          * @param {String} targetId The target drag drop object
60698          * @param {event} e The raw browser event
60699          */
60700         "dragenter" : true,
60701         /**
60702          * @event dragout
60703          * Fires when the dragged row(s) leave another DD target while being dragged
60704          * @param {Grid} this
60705          * @param {Roo.GridDD} dd The drag drop object
60706          * @param {String} targetId The target drag drop object
60707          * @param {event} e The raw browser event
60708          */
60709         "dragout" : true,
60710         /**
60711          * @event rowclass
60712          * Fires when a row is rendered, so you can change add a style to it.
60713          * @param {GridView} gridview   The grid view
60714          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60715          */
60716         'rowclass' : true,
60717
60718         /**
60719          * @event render
60720          * Fires when the grid is rendered
60721          * @param {Grid} grid
60722          */
60723         'render' : true,
60724             /**
60725              * @event select
60726              * Fires when a date is selected
60727              * @param {DatePicker} this
60728              * @param {Date} date The selected date
60729              */
60730         'select': true,
60731         /**
60732              * @event monthchange
60733              * Fires when the displayed month changes 
60734              * @param {DatePicker} this
60735              * @param {Date} date The selected month
60736              */
60737         'monthchange': true,
60738         /**
60739              * @event evententer
60740              * Fires when mouse over an event
60741              * @param {Calendar} this
60742              * @param {event} Event
60743              */
60744         'evententer': true,
60745         /**
60746              * @event eventleave
60747              * Fires when the mouse leaves an
60748              * @param {Calendar} this
60749              * @param {event}
60750              */
60751         'eventleave': true,
60752         /**
60753              * @event eventclick
60754              * Fires when the mouse click an
60755              * @param {Calendar} this
60756              * @param {event}
60757              */
60758         'eventclick': true,
60759         /**
60760              * @event eventrender
60761              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60762              * @param {Calendar} this
60763              * @param {data} data to be modified
60764              */
60765         'eventrender': true
60766         
60767     });
60768
60769     Roo.grid.Grid.superclass.constructor.call(this);
60770     this.on('render', function() {
60771         this.view.el.addClass('x-grid-cal'); 
60772         
60773         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60774
60775     },this);
60776     
60777     if (!Roo.grid.Calendar.style) {
60778         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60779             
60780             
60781             '.x-grid-cal .x-grid-col' :  {
60782                 height: 'auto !important',
60783                 'vertical-align': 'top'
60784             },
60785             '.x-grid-cal  .fc-event-hori' : {
60786                 height: '14px'
60787             }
60788              
60789             
60790         }, Roo.id());
60791     }
60792
60793     
60794     
60795 };
60796 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60797     /**
60798      * @cfg {Store} eventStore The store that loads events.
60799      */
60800     eventStore : 25,
60801
60802      
60803     activeDate : false,
60804     startDay : 0,
60805     autoWidth : true,
60806     monitorWindowResize : false,
60807
60808     
60809     resizeColumns : function() {
60810         var col = (this.view.el.getWidth() / 7) - 3;
60811         // loop through cols, and setWidth
60812         for(var i =0 ; i < 7 ; i++){
60813             this.cm.setColumnWidth(i, col);
60814         }
60815     },
60816      setDate :function(date) {
60817         
60818         Roo.log('setDate?');
60819         
60820         this.resizeColumns();
60821         var vd = this.activeDate;
60822         this.activeDate = date;
60823 //        if(vd && this.el){
60824 //            var t = date.getTime();
60825 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60826 //                Roo.log('using add remove');
60827 //                
60828 //                this.fireEvent('monthchange', this, date);
60829 //                
60830 //                this.cells.removeClass("fc-state-highlight");
60831 //                this.cells.each(function(c){
60832 //                   if(c.dateValue == t){
60833 //                       c.addClass("fc-state-highlight");
60834 //                       setTimeout(function(){
60835 //                            try{c.dom.firstChild.focus();}catch(e){}
60836 //                       }, 50);
60837 //                       return false;
60838 //                   }
60839 //                   return true;
60840 //                });
60841 //                return;
60842 //            }
60843 //        }
60844         
60845         var days = date.getDaysInMonth();
60846         
60847         var firstOfMonth = date.getFirstDateOfMonth();
60848         var startingPos = firstOfMonth.getDay()-this.startDay;
60849         
60850         if(startingPos < this.startDay){
60851             startingPos += 7;
60852         }
60853         
60854         var pm = date.add(Date.MONTH, -1);
60855         var prevStart = pm.getDaysInMonth()-startingPos;
60856 //        
60857         
60858         
60859         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60860         
60861         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60862         //this.cells.addClassOnOver('fc-state-hover');
60863         
60864         var cells = this.cells.elements;
60865         var textEls = this.textNodes;
60866         
60867         //Roo.each(cells, function(cell){
60868         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60869         //});
60870         
60871         days += startingPos;
60872
60873         // convert everything to numbers so it's fast
60874         var day = 86400000;
60875         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60876         //Roo.log(d);
60877         //Roo.log(pm);
60878         //Roo.log(prevStart);
60879         
60880         var today = new Date().clearTime().getTime();
60881         var sel = date.clearTime().getTime();
60882         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60883         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60884         var ddMatch = this.disabledDatesRE;
60885         var ddText = this.disabledDatesText;
60886         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60887         var ddaysText = this.disabledDaysText;
60888         var format = this.format;
60889         
60890         var setCellClass = function(cal, cell){
60891             
60892             //Roo.log('set Cell Class');
60893             cell.title = "";
60894             var t = d.getTime();
60895             
60896             //Roo.log(d);
60897             
60898             
60899             cell.dateValue = t;
60900             if(t == today){
60901                 cell.className += " fc-today";
60902                 cell.className += " fc-state-highlight";
60903                 cell.title = cal.todayText;
60904             }
60905             if(t == sel){
60906                 // disable highlight in other month..
60907                 cell.className += " fc-state-highlight";
60908                 
60909             }
60910             // disabling
60911             if(t < min) {
60912                 //cell.className = " fc-state-disabled";
60913                 cell.title = cal.minText;
60914                 return;
60915             }
60916             if(t > max) {
60917                 //cell.className = " fc-state-disabled";
60918                 cell.title = cal.maxText;
60919                 return;
60920             }
60921             if(ddays){
60922                 if(ddays.indexOf(d.getDay()) != -1){
60923                     // cell.title = ddaysText;
60924                    // cell.className = " fc-state-disabled";
60925                 }
60926             }
60927             if(ddMatch && format){
60928                 var fvalue = d.dateFormat(format);
60929                 if(ddMatch.test(fvalue)){
60930                     cell.title = ddText.replace("%0", fvalue);
60931                    cell.className = " fc-state-disabled";
60932                 }
60933             }
60934             
60935             if (!cell.initialClassName) {
60936                 cell.initialClassName = cell.dom.className;
60937             }
60938             
60939             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60940         };
60941
60942         var i = 0;
60943         
60944         for(; i < startingPos; i++) {
60945             cells[i].dayName =  (++prevStart);
60946             Roo.log(textEls[i]);
60947             d.setDate(d.getDate()+1);
60948             
60949             //cells[i].className = "fc-past fc-other-month";
60950             setCellClass(this, cells[i]);
60951         }
60952         
60953         var intDay = 0;
60954         
60955         for(; i < days; i++){
60956             intDay = i - startingPos + 1;
60957             cells[i].dayName =  (intDay);
60958             d.setDate(d.getDate()+1);
60959             
60960             cells[i].className = ''; // "x-date-active";
60961             setCellClass(this, cells[i]);
60962         }
60963         var extraDays = 0;
60964         
60965         for(; i < 42; i++) {
60966             //textEls[i].innerHTML = (++extraDays);
60967             
60968             d.setDate(d.getDate()+1);
60969             cells[i].dayName = (++extraDays);
60970             cells[i].className = "fc-future fc-other-month";
60971             setCellClass(this, cells[i]);
60972         }
60973         
60974         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60975         
60976         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60977         
60978         // this will cause all the cells to mis
60979         var rows= [];
60980         var i =0;
60981         for (var r = 0;r < 6;r++) {
60982             for (var c =0;c < 7;c++) {
60983                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60984             }    
60985         }
60986         
60987         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60988         for(i=0;i<cells.length;i++) {
60989             
60990             this.cells.elements[i].dayName = cells[i].dayName ;
60991             this.cells.elements[i].className = cells[i].className;
60992             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60993             this.cells.elements[i].title = cells[i].title ;
60994             this.cells.elements[i].dateValue = cells[i].dateValue ;
60995         }
60996         
60997         
60998         
60999         
61000         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61001         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61002         
61003         ////if(totalRows != 6){
61004             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61005            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61006        // }
61007         
61008         this.fireEvent('monthchange', this, date);
61009         
61010         
61011     },
61012  /**
61013      * Returns the grid's SelectionModel.
61014      * @return {SelectionModel}
61015      */
61016     getSelectionModel : function(){
61017         if(!this.selModel){
61018             this.selModel = new Roo.grid.CellSelectionModel();
61019         }
61020         return this.selModel;
61021     },
61022
61023     load: function() {
61024         this.eventStore.load()
61025         
61026         
61027         
61028     },
61029     
61030     findCell : function(dt) {
61031         dt = dt.clearTime().getTime();
61032         var ret = false;
61033         this.cells.each(function(c){
61034             //Roo.log("check " +c.dateValue + '?=' + dt);
61035             if(c.dateValue == dt){
61036                 ret = c;
61037                 return false;
61038             }
61039             return true;
61040         });
61041         
61042         return ret;
61043     },
61044     
61045     findCells : function(rec) {
61046         var s = rec.data.start_dt.clone().clearTime().getTime();
61047        // Roo.log(s);
61048         var e= rec.data.end_dt.clone().clearTime().getTime();
61049        // Roo.log(e);
61050         var ret = [];
61051         this.cells.each(function(c){
61052              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61053             
61054             if(c.dateValue > e){
61055                 return ;
61056             }
61057             if(c.dateValue < s){
61058                 return ;
61059             }
61060             ret.push(c);
61061         });
61062         
61063         return ret;    
61064     },
61065     
61066     findBestRow: function(cells)
61067     {
61068         var ret = 0;
61069         
61070         for (var i =0 ; i < cells.length;i++) {
61071             ret  = Math.max(cells[i].rows || 0,ret);
61072         }
61073         return ret;
61074         
61075     },
61076     
61077     
61078     addItem : function(rec)
61079     {
61080         // look for vertical location slot in
61081         var cells = this.findCells(rec);
61082         
61083         rec.row = this.findBestRow(cells);
61084         
61085         // work out the location.
61086         
61087         var crow = false;
61088         var rows = [];
61089         for(var i =0; i < cells.length; i++) {
61090             if (!crow) {
61091                 crow = {
61092                     start : cells[i],
61093                     end :  cells[i]
61094                 };
61095                 continue;
61096             }
61097             if (crow.start.getY() == cells[i].getY()) {
61098                 // on same row.
61099                 crow.end = cells[i];
61100                 continue;
61101             }
61102             // different row.
61103             rows.push(crow);
61104             crow = {
61105                 start: cells[i],
61106                 end : cells[i]
61107             };
61108             
61109         }
61110         
61111         rows.push(crow);
61112         rec.els = [];
61113         rec.rows = rows;
61114         rec.cells = cells;
61115         for (var i = 0; i < cells.length;i++) {
61116             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61117             
61118         }
61119         
61120         
61121     },
61122     
61123     clearEvents: function() {
61124         
61125         if (!this.eventStore.getCount()) {
61126             return;
61127         }
61128         // reset number of rows in cells.
61129         Roo.each(this.cells.elements, function(c){
61130             c.rows = 0;
61131         });
61132         
61133         this.eventStore.each(function(e) {
61134             this.clearEvent(e);
61135         },this);
61136         
61137     },
61138     
61139     clearEvent : function(ev)
61140     {
61141         if (ev.els) {
61142             Roo.each(ev.els, function(el) {
61143                 el.un('mouseenter' ,this.onEventEnter, this);
61144                 el.un('mouseleave' ,this.onEventLeave, this);
61145                 el.remove();
61146             },this);
61147             ev.els = [];
61148         }
61149     },
61150     
61151     
61152     renderEvent : function(ev,ctr) {
61153         if (!ctr) {
61154              ctr = this.view.el.select('.fc-event-container',true).first();
61155         }
61156         
61157          
61158         this.clearEvent(ev);
61159             //code
61160        
61161         
61162         
61163         ev.els = [];
61164         var cells = ev.cells;
61165         var rows = ev.rows;
61166         this.fireEvent('eventrender', this, ev);
61167         
61168         for(var i =0; i < rows.length; i++) {
61169             
61170             cls = '';
61171             if (i == 0) {
61172                 cls += ' fc-event-start';
61173             }
61174             if ((i+1) == rows.length) {
61175                 cls += ' fc-event-end';
61176             }
61177             
61178             //Roo.log(ev.data);
61179             // how many rows should it span..
61180             var cg = this.eventTmpl.append(ctr,Roo.apply({
61181                 fccls : cls
61182                 
61183             }, ev.data) , true);
61184             
61185             
61186             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61187             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61188             cg.on('click', this.onEventClick, this, ev);
61189             
61190             ev.els.push(cg);
61191             
61192             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61193             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61194             //Roo.log(cg);
61195              
61196             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61197             cg.setWidth(ebox.right - sbox.x -2);
61198         }
61199     },
61200     
61201     renderEvents: function()
61202     {   
61203         // first make sure there is enough space..
61204         
61205         if (!this.eventTmpl) {
61206             this.eventTmpl = new Roo.Template(
61207                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61208                     '<div class="fc-event-inner">' +
61209                         '<span class="fc-event-time">{time}</span>' +
61210                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61211                     '</div>' +
61212                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61213                 '</div>'
61214             );
61215                 
61216         }
61217                
61218         
61219         
61220         this.cells.each(function(c) {
61221             //Roo.log(c.select('.fc-day-content div',true).first());
61222             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61223         });
61224         
61225         var ctr = this.view.el.select('.fc-event-container',true).first();
61226         
61227         var cls;
61228         this.eventStore.each(function(ev){
61229             
61230             this.renderEvent(ev);
61231              
61232              
61233         }, this);
61234         this.view.layout();
61235         
61236     },
61237     
61238     onEventEnter: function (e, el,event,d) {
61239         this.fireEvent('evententer', this, el, event);
61240     },
61241     
61242     onEventLeave: function (e, el,event,d) {
61243         this.fireEvent('eventleave', this, el, event);
61244     },
61245     
61246     onEventClick: function (e, el,event,d) {
61247         this.fireEvent('eventclick', this, el, event);
61248     },
61249     
61250     onMonthChange: function () {
61251         this.store.load();
61252     },
61253     
61254     onLoad: function () {
61255         
61256         //Roo.log('calendar onload');
61257 //         
61258         if(this.eventStore.getCount() > 0){
61259             
61260            
61261             
61262             this.eventStore.each(function(d){
61263                 
61264                 
61265                 // FIXME..
61266                 var add =   d.data;
61267                 if (typeof(add.end_dt) == 'undefined')  {
61268                     Roo.log("Missing End time in calendar data: ");
61269                     Roo.log(d);
61270                     return;
61271                 }
61272                 if (typeof(add.start_dt) == 'undefined')  {
61273                     Roo.log("Missing Start time in calendar data: ");
61274                     Roo.log(d);
61275                     return;
61276                 }
61277                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61278                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61279                 add.id = add.id || d.id;
61280                 add.title = add.title || '??';
61281                 
61282                 this.addItem(d);
61283                 
61284              
61285             },this);
61286         }
61287         
61288         this.renderEvents();
61289     }
61290     
61291
61292 });
61293 /*
61294  grid : {
61295                 xtype: 'Grid',
61296                 xns: Roo.grid,
61297                 listeners : {
61298                     render : function ()
61299                     {
61300                         _this.grid = this;
61301                         
61302                         if (!this.view.el.hasClass('course-timesheet')) {
61303                             this.view.el.addClass('course-timesheet');
61304                         }
61305                         if (this.tsStyle) {
61306                             this.ds.load({});
61307                             return; 
61308                         }
61309                         Roo.log('width');
61310                         Roo.log(_this.grid.view.el.getWidth());
61311                         
61312                         
61313                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61314                             '.course-timesheet .x-grid-row' : {
61315                                 height: '80px'
61316                             },
61317                             '.x-grid-row td' : {
61318                                 'vertical-align' : 0
61319                             },
61320                             '.course-edit-link' : {
61321                                 'color' : 'blue',
61322                                 'text-overflow' : 'ellipsis',
61323                                 'overflow' : 'hidden',
61324                                 'white-space' : 'nowrap',
61325                                 'cursor' : 'pointer'
61326                             },
61327                             '.sub-link' : {
61328                                 'color' : 'green'
61329                             },
61330                             '.de-act-sup-link' : {
61331                                 'color' : 'purple',
61332                                 'text-decoration' : 'line-through'
61333                             },
61334                             '.de-act-link' : {
61335                                 'color' : 'red',
61336                                 'text-decoration' : 'line-through'
61337                             },
61338                             '.course-timesheet .course-highlight' : {
61339                                 'border-top-style': 'dashed !important',
61340                                 'border-bottom-bottom': 'dashed !important'
61341                             },
61342                             '.course-timesheet .course-item' : {
61343                                 'font-family'   : 'tahoma, arial, helvetica',
61344                                 'font-size'     : '11px',
61345                                 'overflow'      : 'hidden',
61346                                 'padding-left'  : '10px',
61347                                 'padding-right' : '10px',
61348                                 'padding-top' : '10px' 
61349                             }
61350                             
61351                         }, Roo.id());
61352                                 this.ds.load({});
61353                     }
61354                 },
61355                 autoWidth : true,
61356                 monitorWindowResize : false,
61357                 cellrenderer : function(v,x,r)
61358                 {
61359                     return v;
61360                 },
61361                 sm : {
61362                     xtype: 'CellSelectionModel',
61363                     xns: Roo.grid
61364                 },
61365                 dataSource : {
61366                     xtype: 'Store',
61367                     xns: Roo.data,
61368                     listeners : {
61369                         beforeload : function (_self, options)
61370                         {
61371                             options.params = options.params || {};
61372                             options.params._month = _this.monthField.getValue();
61373                             options.params.limit = 9999;
61374                             options.params['sort'] = 'when_dt';    
61375                             options.params['dir'] = 'ASC';    
61376                             this.proxy.loadResponse = this.loadResponse;
61377                             Roo.log("load?");
61378                             //this.addColumns();
61379                         },
61380                         load : function (_self, records, options)
61381                         {
61382                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61383                                 // if you click on the translation.. you can edit it...
61384                                 var el = Roo.get(this);
61385                                 var id = el.dom.getAttribute('data-id');
61386                                 var d = el.dom.getAttribute('data-date');
61387                                 var t = el.dom.getAttribute('data-time');
61388                                 //var id = this.child('span').dom.textContent;
61389                                 
61390                                 //Roo.log(this);
61391                                 Pman.Dialog.CourseCalendar.show({
61392                                     id : id,
61393                                     when_d : d,
61394                                     when_t : t,
61395                                     productitem_active : id ? 1 : 0
61396                                 }, function() {
61397                                     _this.grid.ds.load({});
61398                                 });
61399                            
61400                            });
61401                            
61402                            _this.panel.fireEvent('resize', [ '', '' ]);
61403                         }
61404                     },
61405                     loadResponse : function(o, success, response){
61406                             // this is overridden on before load..
61407                             
61408                             Roo.log("our code?");       
61409                             //Roo.log(success);
61410                             //Roo.log(response)
61411                             delete this.activeRequest;
61412                             if(!success){
61413                                 this.fireEvent("loadexception", this, o, response);
61414                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61415                                 return;
61416                             }
61417                             var result;
61418                             try {
61419                                 result = o.reader.read(response);
61420                             }catch(e){
61421                                 Roo.log("load exception?");
61422                                 this.fireEvent("loadexception", this, o, response, e);
61423                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61424                                 return;
61425                             }
61426                             Roo.log("ready...");        
61427                             // loop through result.records;
61428                             // and set this.tdate[date] = [] << array of records..
61429                             _this.tdata  = {};
61430                             Roo.each(result.records, function(r){
61431                                 //Roo.log(r.data);
61432                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61433                                     _this.tdata[r.data.when_dt.format('j')] = [];
61434                                 }
61435                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61436                             });
61437                             
61438                             //Roo.log(_this.tdata);
61439                             
61440                             result.records = [];
61441                             result.totalRecords = 6;
61442                     
61443                             // let's generate some duumy records for the rows.
61444                             //var st = _this.dateField.getValue();
61445                             
61446                             // work out monday..
61447                             //st = st.add(Date.DAY, -1 * st.format('w'));
61448                             
61449                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61450                             
61451                             var firstOfMonth = date.getFirstDayOfMonth();
61452                             var days = date.getDaysInMonth();
61453                             var d = 1;
61454                             var firstAdded = false;
61455                             for (var i = 0; i < result.totalRecords ; i++) {
61456                                 //var d= st.add(Date.DAY, i);
61457                                 var row = {};
61458                                 var added = 0;
61459                                 for(var w = 0 ; w < 7 ; w++){
61460                                     if(!firstAdded && firstOfMonth != w){
61461                                         continue;
61462                                     }
61463                                     if(d > days){
61464                                         continue;
61465                                     }
61466                                     firstAdded = true;
61467                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61468                                     row['weekday'+w] = String.format(
61469                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61470                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61471                                                     d,
61472                                                     date.format('Y-m-')+dd
61473                                                 );
61474                                     added++;
61475                                     if(typeof(_this.tdata[d]) != 'undefined'){
61476                                         Roo.each(_this.tdata[d], function(r){
61477                                             var is_sub = '';
61478                                             var deactive = '';
61479                                             var id = r.id;
61480                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61481                                             if(r.parent_id*1>0){
61482                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61483                                                 id = r.parent_id;
61484                                             }
61485                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61486                                                 deactive = 'de-act-link';
61487                                             }
61488                                             
61489                                             row['weekday'+w] += String.format(
61490                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61491                                                     id, //0
61492                                                     r.product_id_name, //1
61493                                                     r.when_dt.format('h:ia'), //2
61494                                                     is_sub, //3
61495                                                     deactive, //4
61496                                                     desc // 5
61497                                             );
61498                                         });
61499                                     }
61500                                     d++;
61501                                 }
61502                                 
61503                                 // only do this if something added..
61504                                 if(added > 0){ 
61505                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61506                                 }
61507                                 
61508                                 
61509                                 // push it twice. (second one with an hour..
61510                                 
61511                             }
61512                             //Roo.log(result);
61513                             this.fireEvent("load", this, o, o.request.arg);
61514                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61515                         },
61516                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61517                     proxy : {
61518                         xtype: 'HttpProxy',
61519                         xns: Roo.data,
61520                         method : 'GET',
61521                         url : baseURL + '/Roo/Shop_course.php'
61522                     },
61523                     reader : {
61524                         xtype: 'JsonReader',
61525                         xns: Roo.data,
61526                         id : 'id',
61527                         fields : [
61528                             {
61529                                 'name': 'id',
61530                                 'type': 'int'
61531                             },
61532                             {
61533                                 'name': 'when_dt',
61534                                 'type': 'string'
61535                             },
61536                             {
61537                                 'name': 'end_dt',
61538                                 'type': 'string'
61539                             },
61540                             {
61541                                 'name': 'parent_id',
61542                                 'type': 'int'
61543                             },
61544                             {
61545                                 'name': 'product_id',
61546                                 'type': 'int'
61547                             },
61548                             {
61549                                 'name': 'productitem_id',
61550                                 'type': 'int'
61551                             },
61552                             {
61553                                 'name': 'guid',
61554                                 'type': 'int'
61555                             }
61556                         ]
61557                     }
61558                 },
61559                 toolbar : {
61560                     xtype: 'Toolbar',
61561                     xns: Roo,
61562                     items : [
61563                         {
61564                             xtype: 'Button',
61565                             xns: Roo.Toolbar,
61566                             listeners : {
61567                                 click : function (_self, e)
61568                                 {
61569                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61570                                     sd.setMonth(sd.getMonth()-1);
61571                                     _this.monthField.setValue(sd.format('Y-m-d'));
61572                                     _this.grid.ds.load({});
61573                                 }
61574                             },
61575                             text : "Back"
61576                         },
61577                         {
61578                             xtype: 'Separator',
61579                             xns: Roo.Toolbar
61580                         },
61581                         {
61582                             xtype: 'MonthField',
61583                             xns: Roo.form,
61584                             listeners : {
61585                                 render : function (_self)
61586                                 {
61587                                     _this.monthField = _self;
61588                                    // _this.monthField.set  today
61589                                 },
61590                                 select : function (combo, date)
61591                                 {
61592                                     _this.grid.ds.load({});
61593                                 }
61594                             },
61595                             value : (function() { return new Date(); })()
61596                         },
61597                         {
61598                             xtype: 'Separator',
61599                             xns: Roo.Toolbar
61600                         },
61601                         {
61602                             xtype: 'TextItem',
61603                             xns: Roo.Toolbar,
61604                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61605                         },
61606                         {
61607                             xtype: 'Fill',
61608                             xns: Roo.Toolbar
61609                         },
61610                         {
61611                             xtype: 'Button',
61612                             xns: Roo.Toolbar,
61613                             listeners : {
61614                                 click : function (_self, e)
61615                                 {
61616                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61617                                     sd.setMonth(sd.getMonth()+1);
61618                                     _this.monthField.setValue(sd.format('Y-m-d'));
61619                                     _this.grid.ds.load({});
61620                                 }
61621                             },
61622                             text : "Next"
61623                         }
61624                     ]
61625                 },
61626                  
61627             }
61628         };
61629         
61630         *//*
61631  * Based on:
61632  * Ext JS Library 1.1.1
61633  * Copyright(c) 2006-2007, Ext JS, LLC.
61634  *
61635  * Originally Released Under LGPL - original licence link has changed is not relivant.
61636  *
61637  * Fork - LGPL
61638  * <script type="text/javascript">
61639  */
61640  
61641 /**
61642  * @class Roo.LoadMask
61643  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61644  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61645  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61646  * element's UpdateManager load indicator and will be destroyed after the initial load.
61647  * @constructor
61648  * Create a new LoadMask
61649  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61650  * @param {Object} config The config object
61651  */
61652 Roo.LoadMask = function(el, config){
61653     this.el = Roo.get(el);
61654     Roo.apply(this, config);
61655     if(this.store){
61656         this.store.on('beforeload', this.onBeforeLoad, this);
61657         this.store.on('load', this.onLoad, this);
61658         this.store.on('loadexception', this.onLoadException, this);
61659         this.removeMask = false;
61660     }else{
61661         var um = this.el.getUpdateManager();
61662         um.showLoadIndicator = false; // disable the default indicator
61663         um.on('beforeupdate', this.onBeforeLoad, this);
61664         um.on('update', this.onLoad, this);
61665         um.on('failure', this.onLoad, this);
61666         this.removeMask = true;
61667     }
61668 };
61669
61670 Roo.LoadMask.prototype = {
61671     /**
61672      * @cfg {Boolean} removeMask
61673      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61674      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61675      */
61676     removeMask : false,
61677     /**
61678      * @cfg {String} msg
61679      * The text to display in a centered loading message box (defaults to 'Loading...')
61680      */
61681     msg : 'Loading...',
61682     /**
61683      * @cfg {String} msgCls
61684      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61685      */
61686     msgCls : 'x-mask-loading',
61687
61688     /**
61689      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61690      * @type Boolean
61691      */
61692     disabled: false,
61693
61694     /**
61695      * Disables the mask to prevent it from being displayed
61696      */
61697     disable : function(){
61698        this.disabled = true;
61699     },
61700
61701     /**
61702      * Enables the mask so that it can be displayed
61703      */
61704     enable : function(){
61705         this.disabled = false;
61706     },
61707     
61708     onLoadException : function()
61709     {
61710         Roo.log(arguments);
61711         
61712         if (typeof(arguments[3]) != 'undefined') {
61713             Roo.MessageBox.alert("Error loading",arguments[3]);
61714         } 
61715         /*
61716         try {
61717             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61718                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61719             }   
61720         } catch(e) {
61721             
61722         }
61723         */
61724     
61725         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61726     },
61727     // private
61728     onLoad : function()
61729     {
61730         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61731     },
61732
61733     // private
61734     onBeforeLoad : function(){
61735         if(!this.disabled){
61736             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61737         }
61738     },
61739
61740     // private
61741     destroy : function(){
61742         if(this.store){
61743             this.store.un('beforeload', this.onBeforeLoad, this);
61744             this.store.un('load', this.onLoad, this);
61745             this.store.un('loadexception', this.onLoadException, this);
61746         }else{
61747             var um = this.el.getUpdateManager();
61748             um.un('beforeupdate', this.onBeforeLoad, this);
61749             um.un('update', this.onLoad, this);
61750             um.un('failure', this.onLoad, this);
61751         }
61752     }
61753 };/*
61754  * Based on:
61755  * Ext JS Library 1.1.1
61756  * Copyright(c) 2006-2007, Ext JS, LLC.
61757  *
61758  * Originally Released Under LGPL - original licence link has changed is not relivant.
61759  *
61760  * Fork - LGPL
61761  * <script type="text/javascript">
61762  */
61763
61764
61765 /**
61766  * @class Roo.XTemplate
61767  * @extends Roo.Template
61768  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61769 <pre><code>
61770 var t = new Roo.XTemplate(
61771         '&lt;select name="{name}"&gt;',
61772                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61773         '&lt;/select&gt;'
61774 );
61775  
61776 // then append, applying the master template values
61777  </code></pre>
61778  *
61779  * Supported features:
61780  *
61781  *  Tags:
61782
61783 <pre><code>
61784       {a_variable} - output encoded.
61785       {a_variable.format:("Y-m-d")} - call a method on the variable
61786       {a_variable:raw} - unencoded output
61787       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61788       {a_variable:this.method_on_template(...)} - call a method on the template object.
61789  
61790 </code></pre>
61791  *  The tpl tag:
61792 <pre><code>
61793         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61794         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61795         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61796         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61797   
61798         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61799         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61800 </code></pre>
61801  *      
61802  */
61803 Roo.XTemplate = function()
61804 {
61805     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61806     if (this.html) {
61807         this.compile();
61808     }
61809 };
61810
61811
61812 Roo.extend(Roo.XTemplate, Roo.Template, {
61813
61814     /**
61815      * The various sub templates
61816      */
61817     tpls : false,
61818     /**
61819      *
61820      * basic tag replacing syntax
61821      * WORD:WORD()
61822      *
61823      * // you can fake an object call by doing this
61824      *  x.t:(test,tesT) 
61825      * 
61826      */
61827     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61828
61829     /**
61830      * compile the template
61831      *
61832      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61833      *
61834      */
61835     compile: function()
61836     {
61837         var s = this.html;
61838      
61839         s = ['<tpl>', s, '</tpl>'].join('');
61840     
61841         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61842             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61843             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61844             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61845             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61846             m,
61847             id     = 0,
61848             tpls   = [];
61849     
61850         while(true == !!(m = s.match(re))){
61851             var forMatch   = m[0].match(nameRe),
61852                 ifMatch   = m[0].match(ifRe),
61853                 execMatch   = m[0].match(execRe),
61854                 namedMatch   = m[0].match(namedRe),
61855                 
61856                 exp  = null, 
61857                 fn   = null,
61858                 exec = null,
61859                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61860                 
61861             if (ifMatch) {
61862                 // if - puts fn into test..
61863                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61864                 if(exp){
61865                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61866                 }
61867             }
61868             
61869             if (execMatch) {
61870                 // exec - calls a function... returns empty if true is  returned.
61871                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61872                 if(exp){
61873                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61874                 }
61875             }
61876             
61877             
61878             if (name) {
61879                 // for = 
61880                 switch(name){
61881                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61882                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61883                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61884                 }
61885             }
61886             var uid = namedMatch ? namedMatch[1] : id;
61887             
61888             
61889             tpls.push({
61890                 id:     namedMatch ? namedMatch[1] : id,
61891                 target: name,
61892                 exec:   exec,
61893                 test:   fn,
61894                 body:   m[1] || ''
61895             });
61896             if (namedMatch) {
61897                 s = s.replace(m[0], '');
61898             } else { 
61899                 s = s.replace(m[0], '{xtpl'+ id + '}');
61900             }
61901             ++id;
61902         }
61903         this.tpls = [];
61904         for(var i = tpls.length-1; i >= 0; --i){
61905             this.compileTpl(tpls[i]);
61906             this.tpls[tpls[i].id] = tpls[i];
61907         }
61908         this.master = tpls[tpls.length-1];
61909         return this;
61910     },
61911     /**
61912      * same as applyTemplate, except it's done to one of the subTemplates
61913      * when using named templates, you can do:
61914      *
61915      * var str = pl.applySubTemplate('your-name', values);
61916      *
61917      * 
61918      * @param {Number} id of the template
61919      * @param {Object} values to apply to template
61920      * @param {Object} parent (normaly the instance of this object)
61921      */
61922     applySubTemplate : function(id, values, parent)
61923     {
61924         
61925         
61926         var t = this.tpls[id];
61927         
61928         
61929         try { 
61930             if(t.test && !t.test.call(this, values, parent)){
61931                 return '';
61932             }
61933         } catch(e) {
61934             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61935             Roo.log(e.toString());
61936             Roo.log(t.test);
61937             return ''
61938         }
61939         try { 
61940             
61941             if(t.exec && t.exec.call(this, values, parent)){
61942                 return '';
61943             }
61944         } catch(e) {
61945             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61946             Roo.log(e.toString());
61947             Roo.log(t.exec);
61948             return ''
61949         }
61950         try {
61951             var vs = t.target ? t.target.call(this, values, parent) : values;
61952             parent = t.target ? values : parent;
61953             if(t.target && vs instanceof Array){
61954                 var buf = [];
61955                 for(var i = 0, len = vs.length; i < len; i++){
61956                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61957                 }
61958                 return buf.join('');
61959             }
61960             return t.compiled.call(this, vs, parent);
61961         } catch (e) {
61962             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61963             Roo.log(e.toString());
61964             Roo.log(t.compiled);
61965             return '';
61966         }
61967     },
61968
61969     compileTpl : function(tpl)
61970     {
61971         var fm = Roo.util.Format;
61972         var useF = this.disableFormats !== true;
61973         var sep = Roo.isGecko ? "+" : ",";
61974         var undef = function(str) {
61975             Roo.log("Property not found :"  + str);
61976             return '';
61977         };
61978         
61979         var fn = function(m, name, format, args)
61980         {
61981             //Roo.log(arguments);
61982             args = args ? args.replace(/\\'/g,"'") : args;
61983             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61984             if (typeof(format) == 'undefined') {
61985                 format= 'htmlEncode';
61986             }
61987             if (format == 'raw' ) {
61988                 format = false;
61989             }
61990             
61991             if(name.substr(0, 4) == 'xtpl'){
61992                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61993             }
61994             
61995             // build an array of options to determine if value is undefined..
61996             
61997             // basically get 'xxxx.yyyy' then do
61998             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61999             //    (function () { Roo.log("Property not found"); return ''; })() :
62000             //    ......
62001             
62002             var udef_ar = [];
62003             var lookfor = '';
62004             Roo.each(name.split('.'), function(st) {
62005                 lookfor += (lookfor.length ? '.': '') + st;
62006                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62007             });
62008             
62009             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62010             
62011             
62012             if(format && useF){
62013                 
62014                 args = args ? ',' + args : "";
62015                  
62016                 if(format.substr(0, 5) != "this."){
62017                     format = "fm." + format + '(';
62018                 }else{
62019                     format = 'this.call("'+ format.substr(5) + '", ';
62020                     args = ", values";
62021                 }
62022                 
62023                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62024             }
62025              
62026             if (args.length) {
62027                 // called with xxyx.yuu:(test,test)
62028                 // change to ()
62029                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62030             }
62031             // raw.. - :raw modifier..
62032             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62033             
62034         };
62035         var body;
62036         // branched to use + in gecko and [].join() in others
62037         if(Roo.isGecko){
62038             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62039                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62040                     "';};};";
62041         }else{
62042             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62043             body.push(tpl.body.replace(/(\r\n|\n)/g,
62044                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62045             body.push("'].join('');};};");
62046             body = body.join('');
62047         }
62048         
62049         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62050        
62051         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62052         eval(body);
62053         
62054         return this;
62055     },
62056
62057     applyTemplate : function(values){
62058         return this.master.compiled.call(this, values, {});
62059         //var s = this.subs;
62060     },
62061
62062     apply : function(){
62063         return this.applyTemplate.apply(this, arguments);
62064     }
62065
62066  });
62067
62068 Roo.XTemplate.from = function(el){
62069     el = Roo.getDom(el);
62070     return new Roo.XTemplate(el.value || el.innerHTML);
62071 };