Roo/data/DataProxy.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  * This class is an abstract base class for implementations which provide retrieval of
24490  * unformatted data objects.<br>
24491  * <p>
24492  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24493  * (of the appropriate type which knows how to parse the data object) to provide a block of
24494  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24495  * <p>
24496  * Custom implementations must implement the load method as described in
24497  * {@link Roo.data.HttpProxy#load}.
24498  */
24499 Roo.data.DataProxy = function(){
24500     this.addEvents({
24501         /**
24502          * @event beforeload
24503          * Fires before a network request is made to retrieve a data object.
24504          * @param {Object} This DataProxy object.
24505          * @param {Object} params The params parameter to the load function.
24506          */
24507         beforeload : true,
24508         /**
24509          * @event load
24510          * Fires before the load method's callback is called.
24511          * @param {Object} This DataProxy object.
24512          * @param {Object} o The data object.
24513          * @param {Object} arg The callback argument object passed to the load function.
24514          */
24515         load : true,
24516         /**
24517          * @event loadexception
24518          * Fires if an Exception occurs during data retrieval.
24519          * @param {Object} This DataProxy object.
24520          * @param {Object} o The data object.
24521          * @param {Object} arg The callback argument object passed to the load function.
24522          * @param {Object} e The Exception.
24523          */
24524         loadexception : true
24525     });
24526     Roo.data.DataProxy.superclass.constructor.call(this);
24527 };
24528
24529 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24530
24531     /**
24532      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24533      */
24534 /*
24535  * Based on:
24536  * Ext JS Library 1.1.1
24537  * Copyright(c) 2006-2007, Ext JS, LLC.
24538  *
24539  * Originally Released Under LGPL - original licence link has changed is not relivant.
24540  *
24541  * Fork - LGPL
24542  * <script type="text/javascript">
24543  */
24544 /**
24545  * @class Roo.data.MemoryProxy
24546  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24547  * to the Reader when its load method is called.
24548  * @constructor
24549  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24550  */
24551 Roo.data.MemoryProxy = function(data){
24552     if (data.data) {
24553         data = data.data;
24554     }
24555     Roo.data.MemoryProxy.superclass.constructor.call(this);
24556     this.data = data;
24557 };
24558
24559 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24560     
24561     /**
24562      * Load data from the requested source (in this case an in-memory
24563      * data object passed to the constructor), read the data object into
24564      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24565      * process that block using the passed callback.
24566      * @param {Object} params This parameter is not used by the MemoryProxy class.
24567      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24568      * object into a block of Roo.data.Records.
24569      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24570      * The function must be passed <ul>
24571      * <li>The Record block object</li>
24572      * <li>The "arg" argument from the load function</li>
24573      * <li>A boolean success indicator</li>
24574      * </ul>
24575      * @param {Object} scope The scope in which to call the callback
24576      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24577      */
24578     load : function(params, reader, callback, scope, arg){
24579         params = params || {};
24580         var result;
24581         try {
24582             result = reader.readRecords(params.data ? params.data :this.data);
24583         }catch(e){
24584             this.fireEvent("loadexception", this, arg, null, e);
24585             callback.call(scope, null, arg, false);
24586             return;
24587         }
24588         callback.call(scope, result, arg, true);
24589     },
24590     
24591     // private
24592     update : function(params, records){
24593         
24594     }
24595 });/*
24596  * Based on:
24597  * Ext JS Library 1.1.1
24598  * Copyright(c) 2006-2007, Ext JS, LLC.
24599  *
24600  * Originally Released Under LGPL - original licence link has changed is not relivant.
24601  *
24602  * Fork - LGPL
24603  * <script type="text/javascript">
24604  */
24605 /**
24606  * @class Roo.data.HttpProxy
24607  * @extends Roo.data.DataProxy
24608  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24609  * configured to reference a certain URL.<br><br>
24610  * <p>
24611  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24612  * from which the running page was served.<br><br>
24613  * <p>
24614  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24615  * <p>
24616  * Be aware that to enable the browser to parse an XML document, the server must set
24617  * the Content-Type header in the HTTP response to "text/xml".
24618  * @constructor
24619  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24620  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24621  * will be used to make the request.
24622  */
24623 Roo.data.HttpProxy = function(conn){
24624     Roo.data.HttpProxy.superclass.constructor.call(this);
24625     // is conn a conn config or a real conn?
24626     this.conn = conn;
24627     this.useAjax = !conn || !conn.events;
24628   
24629 };
24630
24631 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24632     // thse are take from connection...
24633     
24634     /**
24635      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24636      */
24637     /**
24638      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24639      * extra parameters to each request made by this object. (defaults to undefined)
24640      */
24641     /**
24642      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24643      *  to each request made by this object. (defaults to undefined)
24644      */
24645     /**
24646      * @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)
24647      */
24648     /**
24649      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24650      */
24651      /**
24652      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24653      * @type Boolean
24654      */
24655   
24656
24657     /**
24658      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24659      * @type Boolean
24660      */
24661     /**
24662      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24663      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24664      * a finer-grained basis than the DataProxy events.
24665      */
24666     getConnection : function(){
24667         return this.useAjax ? Roo.Ajax : this.conn;
24668     },
24669
24670     /**
24671      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24672      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24673      * process that block using the passed callback.
24674      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24675      * for the request to the remote server.
24676      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24677      * object into a block of Roo.data.Records.
24678      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24679      * The function must be passed <ul>
24680      * <li>The Record block object</li>
24681      * <li>The "arg" argument from the load function</li>
24682      * <li>A boolean success indicator</li>
24683      * </ul>
24684      * @param {Object} scope The scope in which to call the callback
24685      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24686      */
24687     load : function(params, reader, callback, scope, arg){
24688         if(this.fireEvent("beforeload", this, params) !== false){
24689             var  o = {
24690                 params : params || {},
24691                 request: {
24692                     callback : callback,
24693                     scope : scope,
24694                     arg : arg
24695                 },
24696                 reader: reader,
24697                 callback : this.loadResponse,
24698                 scope: this
24699             };
24700             if(this.useAjax){
24701                 Roo.applyIf(o, this.conn);
24702                 if(this.activeRequest){
24703                     Roo.Ajax.abort(this.activeRequest);
24704                 }
24705                 this.activeRequest = Roo.Ajax.request(o);
24706             }else{
24707                 this.conn.request(o);
24708             }
24709         }else{
24710             callback.call(scope||this, null, arg, false);
24711         }
24712     },
24713
24714     // private
24715     loadResponse : function(o, success, response){
24716         delete this.activeRequest;
24717         if(!success){
24718             this.fireEvent("loadexception", this, o, response);
24719             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24720             return;
24721         }
24722         var result;
24723         try {
24724             result = o.reader.read(response);
24725         }catch(e){
24726             this.fireEvent("loadexception", this, o, response, e);
24727             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24728             return;
24729         }
24730         
24731         this.fireEvent("load", this, o, o.request.arg);
24732         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24733     },
24734
24735     // private
24736     update : function(dataSet){
24737
24738     },
24739
24740     // private
24741     updateResponse : function(dataSet){
24742
24743     }
24744 });/*
24745  * Based on:
24746  * Ext JS Library 1.1.1
24747  * Copyright(c) 2006-2007, Ext JS, LLC.
24748  *
24749  * Originally Released Under LGPL - original licence link has changed is not relivant.
24750  *
24751  * Fork - LGPL
24752  * <script type="text/javascript">
24753  */
24754
24755 /**
24756  * @class Roo.data.ScriptTagProxy
24757  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24758  * other than the originating domain of the running page.<br><br>
24759  * <p>
24760  * <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
24761  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24762  * <p>
24763  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24764  * source code that is used as the source inside a &lt;script> tag.<br><br>
24765  * <p>
24766  * In order for the browser to process the returned data, the server must wrap the data object
24767  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24768  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24769  * depending on whether the callback name was passed:
24770  * <p>
24771  * <pre><code>
24772 boolean scriptTag = false;
24773 String cb = request.getParameter("callback");
24774 if (cb != null) {
24775     scriptTag = true;
24776     response.setContentType("text/javascript");
24777 } else {
24778     response.setContentType("application/x-json");
24779 }
24780 Writer out = response.getWriter();
24781 if (scriptTag) {
24782     out.write(cb + "(");
24783 }
24784 out.print(dataBlock.toJsonString());
24785 if (scriptTag) {
24786     out.write(");");
24787 }
24788 </pre></code>
24789  *
24790  * @constructor
24791  * @param {Object} config A configuration object.
24792  */
24793 Roo.data.ScriptTagProxy = function(config){
24794     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24795     Roo.apply(this, config);
24796     this.head = document.getElementsByTagName("head")[0];
24797 };
24798
24799 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24800
24801 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24802     /**
24803      * @cfg {String} url The URL from which to request the data object.
24804      */
24805     /**
24806      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24807      */
24808     timeout : 30000,
24809     /**
24810      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24811      * the server the name of the callback function set up by the load call to process the returned data object.
24812      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24813      * javascript output which calls this named function passing the data object as its only parameter.
24814      */
24815     callbackParam : "callback",
24816     /**
24817      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24818      * name to the request.
24819      */
24820     nocache : true,
24821
24822     /**
24823      * Load data from the configured URL, read the data object into
24824      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24825      * process that block using the passed callback.
24826      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24827      * for the request to the remote server.
24828      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24829      * object into a block of Roo.data.Records.
24830      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24831      * The function must be passed <ul>
24832      * <li>The Record block object</li>
24833      * <li>The "arg" argument from the load function</li>
24834      * <li>A boolean success indicator</li>
24835      * </ul>
24836      * @param {Object} scope The scope in which to call the callback
24837      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24838      */
24839     load : function(params, reader, callback, scope, arg){
24840         if(this.fireEvent("beforeload", this, params) !== false){
24841
24842             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24843
24844             var url = this.url;
24845             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24846             if(this.nocache){
24847                 url += "&_dc=" + (new Date().getTime());
24848             }
24849             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24850             var trans = {
24851                 id : transId,
24852                 cb : "stcCallback"+transId,
24853                 scriptId : "stcScript"+transId,
24854                 params : params,
24855                 arg : arg,
24856                 url : url,
24857                 callback : callback,
24858                 scope : scope,
24859                 reader : reader
24860             };
24861             var conn = this;
24862
24863             window[trans.cb] = function(o){
24864                 conn.handleResponse(o, trans);
24865             };
24866
24867             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24868
24869             if(this.autoAbort !== false){
24870                 this.abort();
24871             }
24872
24873             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24874
24875             var script = document.createElement("script");
24876             script.setAttribute("src", url);
24877             script.setAttribute("type", "text/javascript");
24878             script.setAttribute("id", trans.scriptId);
24879             this.head.appendChild(script);
24880
24881             this.trans = trans;
24882         }else{
24883             callback.call(scope||this, null, arg, false);
24884         }
24885     },
24886
24887     // private
24888     isLoading : function(){
24889         return this.trans ? true : false;
24890     },
24891
24892     /**
24893      * Abort the current server request.
24894      */
24895     abort : function(){
24896         if(this.isLoading()){
24897             this.destroyTrans(this.trans);
24898         }
24899     },
24900
24901     // private
24902     destroyTrans : function(trans, isLoaded){
24903         this.head.removeChild(document.getElementById(trans.scriptId));
24904         clearTimeout(trans.timeoutId);
24905         if(isLoaded){
24906             window[trans.cb] = undefined;
24907             try{
24908                 delete window[trans.cb];
24909             }catch(e){}
24910         }else{
24911             // if hasn't been loaded, wait for load to remove it to prevent script error
24912             window[trans.cb] = function(){
24913                 window[trans.cb] = undefined;
24914                 try{
24915                     delete window[trans.cb];
24916                 }catch(e){}
24917             };
24918         }
24919     },
24920
24921     // private
24922     handleResponse : function(o, trans){
24923         this.trans = false;
24924         this.destroyTrans(trans, true);
24925         var result;
24926         try {
24927             result = trans.reader.readRecords(o);
24928         }catch(e){
24929             this.fireEvent("loadexception", this, o, trans.arg, e);
24930             trans.callback.call(trans.scope||window, null, trans.arg, false);
24931             return;
24932         }
24933         this.fireEvent("load", this, o, trans.arg);
24934         trans.callback.call(trans.scope||window, result, trans.arg, true);
24935     },
24936
24937     // private
24938     handleFailure : function(trans){
24939         this.trans = false;
24940         this.destroyTrans(trans, false);
24941         this.fireEvent("loadexception", this, null, trans.arg);
24942         trans.callback.call(trans.scope||window, null, trans.arg, false);
24943     }
24944 });/*
24945  * Based on:
24946  * Ext JS Library 1.1.1
24947  * Copyright(c) 2006-2007, Ext JS, LLC.
24948  *
24949  * Originally Released Under LGPL - original licence link has changed is not relivant.
24950  *
24951  * Fork - LGPL
24952  * <script type="text/javascript">
24953  */
24954
24955 /**
24956  * @class Roo.data.JsonReader
24957  * @extends Roo.data.DataReader
24958  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24959  * based on mappings in a provided Roo.data.Record constructor.
24960  * 
24961  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24962  * in the reply previously. 
24963  * 
24964  * <p>
24965  * Example code:
24966  * <pre><code>
24967 var RecordDef = Roo.data.Record.create([
24968     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24969     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24970 ]);
24971 var myReader = new Roo.data.JsonReader({
24972     totalProperty: "results",    // The property which contains the total dataset size (optional)
24973     root: "rows",                // The property which contains an Array of row objects
24974     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24975 }, RecordDef);
24976 </code></pre>
24977  * <p>
24978  * This would consume a JSON file like this:
24979  * <pre><code>
24980 { 'results': 2, 'rows': [
24981     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24982     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24983 }
24984 </code></pre>
24985  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24986  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24987  * paged from the remote server.
24988  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24989  * @cfg {String} root name of the property which contains the Array of row objects.
24990  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24991  * @cfg {Array} fields Array of field definition objects
24992  * @constructor
24993  * Create a new JsonReader
24994  * @param {Object} meta Metadata configuration options
24995  * @param {Object} recordType Either an Array of field definition objects,
24996  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24997  */
24998 Roo.data.JsonReader = function(meta, recordType){
24999     
25000     meta = meta || {};
25001     // set some defaults:
25002     Roo.applyIf(meta, {
25003         totalProperty: 'total',
25004         successProperty : 'success',
25005         root : 'data',
25006         id : 'id'
25007     });
25008     
25009     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25010 };
25011 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25012     
25013     readerType : 'Json',
25014     
25015     /**
25016      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25017      * Used by Store query builder to append _requestMeta to params.
25018      * 
25019      */
25020     metaFromRemote : false,
25021     /**
25022      * This method is only used by a DataProxy which has retrieved data from a remote server.
25023      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25024      * @return {Object} data A data block which is used by an Roo.data.Store object as
25025      * a cache of Roo.data.Records.
25026      */
25027     read : function(response){
25028         var json = response.responseText;
25029        
25030         var o = /* eval:var:o */ eval("("+json+")");
25031         if(!o) {
25032             throw {message: "JsonReader.read: Json object not found"};
25033         }
25034         
25035         if(o.metaData){
25036             
25037             delete this.ef;
25038             this.metaFromRemote = true;
25039             this.meta = o.metaData;
25040             this.recordType = Roo.data.Record.create(o.metaData.fields);
25041             this.onMetaChange(this.meta, this.recordType, o);
25042         }
25043         return this.readRecords(o);
25044     },
25045
25046     // private function a store will implement
25047     onMetaChange : function(meta, recordType, o){
25048
25049     },
25050
25051     /**
25052          * @ignore
25053          */
25054     simpleAccess: function(obj, subsc) {
25055         return obj[subsc];
25056     },
25057
25058         /**
25059          * @ignore
25060          */
25061     getJsonAccessor: function(){
25062         var re = /[\[\.]/;
25063         return function(expr) {
25064             try {
25065                 return(re.test(expr))
25066                     ? new Function("obj", "return obj." + expr)
25067                     : function(obj){
25068                         return obj[expr];
25069                     };
25070             } catch(e){}
25071             return Roo.emptyFn;
25072         };
25073     }(),
25074
25075     /**
25076      * Create a data block containing Roo.data.Records from an XML document.
25077      * @param {Object} o An object which contains an Array of row objects in the property specified
25078      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25079      * which contains the total size of the dataset.
25080      * @return {Object} data A data block which is used by an Roo.data.Store object as
25081      * a cache of Roo.data.Records.
25082      */
25083     readRecords : function(o){
25084         /**
25085          * After any data loads, the raw JSON data is available for further custom processing.
25086          * @type Object
25087          */
25088         this.o = o;
25089         var s = this.meta, Record = this.recordType,
25090             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25091
25092 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25093         if (!this.ef) {
25094             if(s.totalProperty) {
25095                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25096                 }
25097                 if(s.successProperty) {
25098                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25099                 }
25100                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25101                 if (s.id) {
25102                         var g = this.getJsonAccessor(s.id);
25103                         this.getId = function(rec) {
25104                                 var r = g(rec);  
25105                                 return (r === undefined || r === "") ? null : r;
25106                         };
25107                 } else {
25108                         this.getId = function(){return null;};
25109                 }
25110             this.ef = [];
25111             for(var jj = 0; jj < fl; jj++){
25112                 f = fi[jj];
25113                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25114                 this.ef[jj] = this.getJsonAccessor(map);
25115             }
25116         }
25117
25118         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25119         if(s.totalProperty){
25120             var vt = parseInt(this.getTotal(o), 10);
25121             if(!isNaN(vt)){
25122                 totalRecords = vt;
25123             }
25124         }
25125         if(s.successProperty){
25126             var vs = this.getSuccess(o);
25127             if(vs === false || vs === 'false'){
25128                 success = false;
25129             }
25130         }
25131         var records = [];
25132         for(var i = 0; i < c; i++){
25133                 var n = root[i];
25134             var values = {};
25135             var id = this.getId(n);
25136             for(var j = 0; j < fl; j++){
25137                 f = fi[j];
25138             var v = this.ef[j](n);
25139             if (!f.convert) {
25140                 Roo.log('missing convert for ' + f.name);
25141                 Roo.log(f);
25142                 continue;
25143             }
25144             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25145             }
25146             var record = new Record(values, id);
25147             record.json = n;
25148             records[i] = record;
25149         }
25150         return {
25151             raw : o,
25152             success : success,
25153             records : records,
25154             totalRecords : totalRecords
25155         };
25156     },
25157     // used when loading children.. @see loadDataFromChildren
25158     toLoadData: function(rec)
25159     {
25160         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25161         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25162         return { data : data, total : data.length };
25163         
25164     }
25165 });/*
25166  * Based on:
25167  * Ext JS Library 1.1.1
25168  * Copyright(c) 2006-2007, Ext JS, LLC.
25169  *
25170  * Originally Released Under LGPL - original licence link has changed is not relivant.
25171  *
25172  * Fork - LGPL
25173  * <script type="text/javascript">
25174  */
25175
25176 /**
25177  * @class Roo.data.XmlReader
25178  * @extends Roo.data.DataReader
25179  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25180  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25181  * <p>
25182  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25183  * header in the HTTP response must be set to "text/xml".</em>
25184  * <p>
25185  * Example code:
25186  * <pre><code>
25187 var RecordDef = Roo.data.Record.create([
25188    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25189    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25190 ]);
25191 var myReader = new Roo.data.XmlReader({
25192    totalRecords: "results", // The element which contains the total dataset size (optional)
25193    record: "row",           // The repeated element which contains row information
25194    id: "id"                 // The element within the row that provides an ID for the record (optional)
25195 }, RecordDef);
25196 </code></pre>
25197  * <p>
25198  * This would consume an XML file like this:
25199  * <pre><code>
25200 &lt;?xml?>
25201 &lt;dataset>
25202  &lt;results>2&lt;/results>
25203  &lt;row>
25204    &lt;id>1&lt;/id>
25205    &lt;name>Bill&lt;/name>
25206    &lt;occupation>Gardener&lt;/occupation>
25207  &lt;/row>
25208  &lt;row>
25209    &lt;id>2&lt;/id>
25210    &lt;name>Ben&lt;/name>
25211    &lt;occupation>Horticulturalist&lt;/occupation>
25212  &lt;/row>
25213 &lt;/dataset>
25214 </code></pre>
25215  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25216  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25217  * paged from the remote server.
25218  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25219  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25220  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25221  * a record identifier value.
25222  * @constructor
25223  * Create a new XmlReader
25224  * @param {Object} meta Metadata configuration options
25225  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25226  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25227  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25228  */
25229 Roo.data.XmlReader = function(meta, recordType){
25230     meta = meta || {};
25231     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25232 };
25233 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25234     
25235     readerType : 'Xml',
25236     
25237     /**
25238      * This method is only used by a DataProxy which has retrieved data from a remote server.
25239          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25240          * to contain a method called 'responseXML' that returns an XML document object.
25241      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25242      * a cache of Roo.data.Records.
25243      */
25244     read : function(response){
25245         var doc = response.responseXML;
25246         if(!doc) {
25247             throw {message: "XmlReader.read: XML Document not available"};
25248         }
25249         return this.readRecords(doc);
25250     },
25251
25252     /**
25253      * Create a data block containing Roo.data.Records from an XML document.
25254          * @param {Object} doc A parsed XML document.
25255      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25256      * a cache of Roo.data.Records.
25257      */
25258     readRecords : function(doc){
25259         /**
25260          * After any data loads/reads, the raw XML Document is available for further custom processing.
25261          * @type XMLDocument
25262          */
25263         this.xmlData = doc;
25264         var root = doc.documentElement || doc;
25265         var q = Roo.DomQuery;
25266         var recordType = this.recordType, fields = recordType.prototype.fields;
25267         var sid = this.meta.id;
25268         var totalRecords = 0, success = true;
25269         if(this.meta.totalRecords){
25270             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25271         }
25272         
25273         if(this.meta.success){
25274             var sv = q.selectValue(this.meta.success, root, true);
25275             success = sv !== false && sv !== 'false';
25276         }
25277         var records = [];
25278         var ns = q.select(this.meta.record, root);
25279         for(var i = 0, len = ns.length; i < len; i++) {
25280                 var n = ns[i];
25281                 var values = {};
25282                 var id = sid ? q.selectValue(sid, n) : undefined;
25283                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25284                     var f = fields.items[j];
25285                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25286                     v = f.convert(v);
25287                     values[f.name] = v;
25288                 }
25289                 var record = new recordType(values, id);
25290                 record.node = n;
25291                 records[records.length] = record;
25292             }
25293
25294             return {
25295                 success : success,
25296                 records : records,
25297                 totalRecords : totalRecords || records.length
25298             };
25299     }
25300 });/*
25301  * Based on:
25302  * Ext JS Library 1.1.1
25303  * Copyright(c) 2006-2007, Ext JS, LLC.
25304  *
25305  * Originally Released Under LGPL - original licence link has changed is not relivant.
25306  *
25307  * Fork - LGPL
25308  * <script type="text/javascript">
25309  */
25310
25311 /**
25312  * @class Roo.data.ArrayReader
25313  * @extends Roo.data.DataReader
25314  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25315  * Each element of that Array represents a row of data fields. The
25316  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25317  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25318  * <p>
25319  * Example code:.
25320  * <pre><code>
25321 var RecordDef = Roo.data.Record.create([
25322     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25323     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25324 ]);
25325 var myReader = new Roo.data.ArrayReader({
25326     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25327 }, RecordDef);
25328 </code></pre>
25329  * <p>
25330  * This would consume an Array like this:
25331  * <pre><code>
25332 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25333   </code></pre>
25334  
25335  * @constructor
25336  * Create a new JsonReader
25337  * @param {Object} meta Metadata configuration options.
25338  * @param {Object|Array} recordType Either an Array of field definition objects
25339  * 
25340  * @cfg {Array} fields Array of field definition objects
25341  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25342  * as specified to {@link Roo.data.Record#create},
25343  * or an {@link Roo.data.Record} object
25344  *
25345  * 
25346  * created using {@link Roo.data.Record#create}.
25347  */
25348 Roo.data.ArrayReader = function(meta, recordType)
25349 {    
25350     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25351 };
25352
25353 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25354     
25355       /**
25356      * Create a data block containing Roo.data.Records from an XML document.
25357      * @param {Object} o An Array of row objects which represents the dataset.
25358      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25359      * a cache of Roo.data.Records.
25360      */
25361     readRecords : function(o)
25362     {
25363         var sid = this.meta ? this.meta.id : null;
25364         var recordType = this.recordType, fields = recordType.prototype.fields;
25365         var records = [];
25366         var root = o;
25367         for(var i = 0; i < root.length; i++){
25368             var n = root[i];
25369             var values = {};
25370             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25371             for(var j = 0, jlen = fields.length; j < jlen; j++){
25372                 var f = fields.items[j];
25373                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25374                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25375                 v = f.convert(v);
25376                 values[f.name] = v;
25377             }
25378             var record = new recordType(values, id);
25379             record.json = n;
25380             records[records.length] = record;
25381         }
25382         return {
25383             records : records,
25384             totalRecords : records.length
25385         };
25386     },
25387     // used when loading children.. @see loadDataFromChildren
25388     toLoadData: function(rec)
25389     {
25390         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25391         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25392         
25393     }
25394     
25395     
25396 });/*
25397  * Based on:
25398  * Ext JS Library 1.1.1
25399  * Copyright(c) 2006-2007, Ext JS, LLC.
25400  *
25401  * Originally Released Under LGPL - original licence link has changed is not relivant.
25402  *
25403  * Fork - LGPL
25404  * <script type="text/javascript">
25405  */
25406
25407
25408 /**
25409  * @class Roo.data.Tree
25410  * @extends Roo.util.Observable
25411  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25412  * in the tree have most standard DOM functionality.
25413  * @constructor
25414  * @param {Node} root (optional) The root node
25415  */
25416 Roo.data.Tree = function(root){
25417    this.nodeHash = {};
25418    /**
25419     * The root node for this tree
25420     * @type Node
25421     */
25422    this.root = null;
25423    if(root){
25424        this.setRootNode(root);
25425    }
25426    this.addEvents({
25427        /**
25428         * @event append
25429         * Fires when a new child node is appended to a node in this tree.
25430         * @param {Tree} tree The owner tree
25431         * @param {Node} parent The parent node
25432         * @param {Node} node The newly appended node
25433         * @param {Number} index The index of the newly appended node
25434         */
25435        "append" : true,
25436        /**
25437         * @event remove
25438         * Fires when a child node is removed from a node in this tree.
25439         * @param {Tree} tree The owner tree
25440         * @param {Node} parent The parent node
25441         * @param {Node} node The child node removed
25442         */
25443        "remove" : true,
25444        /**
25445         * @event move
25446         * Fires when a node is moved to a new location in the tree
25447         * @param {Tree} tree The owner tree
25448         * @param {Node} node The node moved
25449         * @param {Node} oldParent The old parent of this node
25450         * @param {Node} newParent The new parent of this node
25451         * @param {Number} index The index it was moved to
25452         */
25453        "move" : true,
25454        /**
25455         * @event insert
25456         * Fires when a new child node is inserted in a node in this tree.
25457         * @param {Tree} tree The owner tree
25458         * @param {Node} parent The parent node
25459         * @param {Node} node The child node inserted
25460         * @param {Node} refNode The child node the node was inserted before
25461         */
25462        "insert" : true,
25463        /**
25464         * @event beforeappend
25465         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25466         * @param {Tree} tree The owner tree
25467         * @param {Node} parent The parent node
25468         * @param {Node} node The child node to be appended
25469         */
25470        "beforeappend" : true,
25471        /**
25472         * @event beforeremove
25473         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25474         * @param {Tree} tree The owner tree
25475         * @param {Node} parent The parent node
25476         * @param {Node} node The child node to be removed
25477         */
25478        "beforeremove" : true,
25479        /**
25480         * @event beforemove
25481         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25482         * @param {Tree} tree The owner tree
25483         * @param {Node} node The node being moved
25484         * @param {Node} oldParent The parent of the node
25485         * @param {Node} newParent The new parent the node is moving to
25486         * @param {Number} index The index it is being moved to
25487         */
25488        "beforemove" : true,
25489        /**
25490         * @event beforeinsert
25491         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25492         * @param {Tree} tree The owner tree
25493         * @param {Node} parent The parent node
25494         * @param {Node} node The child node to be inserted
25495         * @param {Node} refNode The child node the node is being inserted before
25496         */
25497        "beforeinsert" : true
25498    });
25499
25500     Roo.data.Tree.superclass.constructor.call(this);
25501 };
25502
25503 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25504     pathSeparator: "/",
25505
25506     proxyNodeEvent : function(){
25507         return this.fireEvent.apply(this, arguments);
25508     },
25509
25510     /**
25511      * Returns the root node for this tree.
25512      * @return {Node}
25513      */
25514     getRootNode : function(){
25515         return this.root;
25516     },
25517
25518     /**
25519      * Sets the root node for this tree.
25520      * @param {Node} node
25521      * @return {Node}
25522      */
25523     setRootNode : function(node){
25524         this.root = node;
25525         node.ownerTree = this;
25526         node.isRoot = true;
25527         this.registerNode(node);
25528         return node;
25529     },
25530
25531     /**
25532      * Gets a node in this tree by its id.
25533      * @param {String} id
25534      * @return {Node}
25535      */
25536     getNodeById : function(id){
25537         return this.nodeHash[id];
25538     },
25539
25540     registerNode : function(node){
25541         this.nodeHash[node.id] = node;
25542     },
25543
25544     unregisterNode : function(node){
25545         delete this.nodeHash[node.id];
25546     },
25547
25548     toString : function(){
25549         return "[Tree"+(this.id?" "+this.id:"")+"]";
25550     }
25551 });
25552
25553 /**
25554  * @class Roo.data.Node
25555  * @extends Roo.util.Observable
25556  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25557  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25558  * @constructor
25559  * @param {Object} attributes The attributes/config for the node
25560  */
25561 Roo.data.Node = function(attributes){
25562     /**
25563      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25564      * @type {Object}
25565      */
25566     this.attributes = attributes || {};
25567     this.leaf = this.attributes.leaf;
25568     /**
25569      * The node id. @type String
25570      */
25571     this.id = this.attributes.id;
25572     if(!this.id){
25573         this.id = Roo.id(null, "ynode-");
25574         this.attributes.id = this.id;
25575     }
25576      
25577     
25578     /**
25579      * All child nodes of this node. @type Array
25580      */
25581     this.childNodes = [];
25582     if(!this.childNodes.indexOf){ // indexOf is a must
25583         this.childNodes.indexOf = function(o){
25584             for(var i = 0, len = this.length; i < len; i++){
25585                 if(this[i] == o) {
25586                     return i;
25587                 }
25588             }
25589             return -1;
25590         };
25591     }
25592     /**
25593      * The parent node for this node. @type Node
25594      */
25595     this.parentNode = null;
25596     /**
25597      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25598      */
25599     this.firstChild = null;
25600     /**
25601      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25602      */
25603     this.lastChild = null;
25604     /**
25605      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25606      */
25607     this.previousSibling = null;
25608     /**
25609      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25610      */
25611     this.nextSibling = null;
25612
25613     this.addEvents({
25614        /**
25615         * @event append
25616         * Fires when a new child node is appended
25617         * @param {Tree} tree The owner tree
25618         * @param {Node} this This node
25619         * @param {Node} node The newly appended node
25620         * @param {Number} index The index of the newly appended node
25621         */
25622        "append" : true,
25623        /**
25624         * @event remove
25625         * Fires when a child node is removed
25626         * @param {Tree} tree The owner tree
25627         * @param {Node} this This node
25628         * @param {Node} node The removed node
25629         */
25630        "remove" : true,
25631        /**
25632         * @event move
25633         * Fires when this node is moved to a new location in the tree
25634         * @param {Tree} tree The owner tree
25635         * @param {Node} this This node
25636         * @param {Node} oldParent The old parent of this node
25637         * @param {Node} newParent The new parent of this node
25638         * @param {Number} index The index it was moved to
25639         */
25640        "move" : true,
25641        /**
25642         * @event insert
25643         * Fires when a new child node is inserted.
25644         * @param {Tree} tree The owner tree
25645         * @param {Node} this This node
25646         * @param {Node} node The child node inserted
25647         * @param {Node} refNode The child node the node was inserted before
25648         */
25649        "insert" : true,
25650        /**
25651         * @event beforeappend
25652         * Fires before a new child is appended, return false to cancel the append.
25653         * @param {Tree} tree The owner tree
25654         * @param {Node} this This node
25655         * @param {Node} node The child node to be appended
25656         */
25657        "beforeappend" : true,
25658        /**
25659         * @event beforeremove
25660         * Fires before a child is removed, return false to cancel the remove.
25661         * @param {Tree} tree The owner tree
25662         * @param {Node} this This node
25663         * @param {Node} node The child node to be removed
25664         */
25665        "beforeremove" : true,
25666        /**
25667         * @event beforemove
25668         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25669         * @param {Tree} tree The owner tree
25670         * @param {Node} this This node
25671         * @param {Node} oldParent The parent of this node
25672         * @param {Node} newParent The new parent this node is moving to
25673         * @param {Number} index The index it is being moved to
25674         */
25675        "beforemove" : true,
25676        /**
25677         * @event beforeinsert
25678         * Fires before a new child is inserted, return false to cancel the insert.
25679         * @param {Tree} tree The owner tree
25680         * @param {Node} this This node
25681         * @param {Node} node The child node to be inserted
25682         * @param {Node} refNode The child node the node is being inserted before
25683         */
25684        "beforeinsert" : true
25685    });
25686     this.listeners = this.attributes.listeners;
25687     Roo.data.Node.superclass.constructor.call(this);
25688 };
25689
25690 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25691     fireEvent : function(evtName){
25692         // first do standard event for this node
25693         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25694             return false;
25695         }
25696         // then bubble it up to the tree if the event wasn't cancelled
25697         var ot = this.getOwnerTree();
25698         if(ot){
25699             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25700                 return false;
25701             }
25702         }
25703         return true;
25704     },
25705
25706     /**
25707      * Returns true if this node is a leaf
25708      * @return {Boolean}
25709      */
25710     isLeaf : function(){
25711         return this.leaf === true;
25712     },
25713
25714     // private
25715     setFirstChild : function(node){
25716         this.firstChild = node;
25717     },
25718
25719     //private
25720     setLastChild : function(node){
25721         this.lastChild = node;
25722     },
25723
25724
25725     /**
25726      * Returns true if this node is the last child of its parent
25727      * @return {Boolean}
25728      */
25729     isLast : function(){
25730        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25731     },
25732
25733     /**
25734      * Returns true if this node is the first child of its parent
25735      * @return {Boolean}
25736      */
25737     isFirst : function(){
25738        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25739     },
25740
25741     hasChildNodes : function(){
25742         return !this.isLeaf() && this.childNodes.length > 0;
25743     },
25744
25745     /**
25746      * Insert node(s) as the last child node of this node.
25747      * @param {Node/Array} node The node or Array of nodes to append
25748      * @return {Node} The appended node if single append, or null if an array was passed
25749      */
25750     appendChild : function(node){
25751         var multi = false;
25752         if(node instanceof Array){
25753             multi = node;
25754         }else if(arguments.length > 1){
25755             multi = arguments;
25756         }
25757         
25758         // if passed an array or multiple args do them one by one
25759         if(multi){
25760             for(var i = 0, len = multi.length; i < len; i++) {
25761                 this.appendChild(multi[i]);
25762             }
25763         }else{
25764             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25765                 return false;
25766             }
25767             var index = this.childNodes.length;
25768             var oldParent = node.parentNode;
25769             // it's a move, make sure we move it cleanly
25770             if(oldParent){
25771                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25772                     return false;
25773                 }
25774                 oldParent.removeChild(node);
25775             }
25776             
25777             index = this.childNodes.length;
25778             if(index == 0){
25779                 this.setFirstChild(node);
25780             }
25781             this.childNodes.push(node);
25782             node.parentNode = this;
25783             var ps = this.childNodes[index-1];
25784             if(ps){
25785                 node.previousSibling = ps;
25786                 ps.nextSibling = node;
25787             }else{
25788                 node.previousSibling = null;
25789             }
25790             node.nextSibling = null;
25791             this.setLastChild(node);
25792             node.setOwnerTree(this.getOwnerTree());
25793             this.fireEvent("append", this.ownerTree, this, node, index);
25794             if(this.ownerTree) {
25795                 this.ownerTree.fireEvent("appendnode", this, node, index);
25796             }
25797             if(oldParent){
25798                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25799             }
25800             return node;
25801         }
25802     },
25803
25804     /**
25805      * Removes a child node from this node.
25806      * @param {Node} node The node to remove
25807      * @return {Node} The removed node
25808      */
25809     removeChild : function(node){
25810         var index = this.childNodes.indexOf(node);
25811         if(index == -1){
25812             return false;
25813         }
25814         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25815             return false;
25816         }
25817
25818         // remove it from childNodes collection
25819         this.childNodes.splice(index, 1);
25820
25821         // update siblings
25822         if(node.previousSibling){
25823             node.previousSibling.nextSibling = node.nextSibling;
25824         }
25825         if(node.nextSibling){
25826             node.nextSibling.previousSibling = node.previousSibling;
25827         }
25828
25829         // update child refs
25830         if(this.firstChild == node){
25831             this.setFirstChild(node.nextSibling);
25832         }
25833         if(this.lastChild == node){
25834             this.setLastChild(node.previousSibling);
25835         }
25836
25837         node.setOwnerTree(null);
25838         // clear any references from the node
25839         node.parentNode = null;
25840         node.previousSibling = null;
25841         node.nextSibling = null;
25842         this.fireEvent("remove", this.ownerTree, this, node);
25843         return node;
25844     },
25845
25846     /**
25847      * Inserts the first node before the second node in this nodes childNodes collection.
25848      * @param {Node} node The node to insert
25849      * @param {Node} refNode The node to insert before (if null the node is appended)
25850      * @return {Node} The inserted node
25851      */
25852     insertBefore : function(node, refNode){
25853         if(!refNode){ // like standard Dom, refNode can be null for append
25854             return this.appendChild(node);
25855         }
25856         // nothing to do
25857         if(node == refNode){
25858             return false;
25859         }
25860
25861         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25862             return false;
25863         }
25864         var index = this.childNodes.indexOf(refNode);
25865         var oldParent = node.parentNode;
25866         var refIndex = index;
25867
25868         // when moving internally, indexes will change after remove
25869         if(oldParent == this && this.childNodes.indexOf(node) < index){
25870             refIndex--;
25871         }
25872
25873         // it's a move, make sure we move it cleanly
25874         if(oldParent){
25875             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25876                 return false;
25877             }
25878             oldParent.removeChild(node);
25879         }
25880         if(refIndex == 0){
25881             this.setFirstChild(node);
25882         }
25883         this.childNodes.splice(refIndex, 0, node);
25884         node.parentNode = this;
25885         var ps = this.childNodes[refIndex-1];
25886         if(ps){
25887             node.previousSibling = ps;
25888             ps.nextSibling = node;
25889         }else{
25890             node.previousSibling = null;
25891         }
25892         node.nextSibling = refNode;
25893         refNode.previousSibling = node;
25894         node.setOwnerTree(this.getOwnerTree());
25895         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25896         if(oldParent){
25897             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25898         }
25899         return node;
25900     },
25901
25902     /**
25903      * Returns the child node at the specified index.
25904      * @param {Number} index
25905      * @return {Node}
25906      */
25907     item : function(index){
25908         return this.childNodes[index];
25909     },
25910
25911     /**
25912      * Replaces one child node in this node with another.
25913      * @param {Node} newChild The replacement node
25914      * @param {Node} oldChild The node to replace
25915      * @return {Node} The replaced node
25916      */
25917     replaceChild : function(newChild, oldChild){
25918         this.insertBefore(newChild, oldChild);
25919         this.removeChild(oldChild);
25920         return oldChild;
25921     },
25922
25923     /**
25924      * Returns the index of a child node
25925      * @param {Node} node
25926      * @return {Number} The index of the node or -1 if it was not found
25927      */
25928     indexOf : function(child){
25929         return this.childNodes.indexOf(child);
25930     },
25931
25932     /**
25933      * Returns the tree this node is in.
25934      * @return {Tree}
25935      */
25936     getOwnerTree : function(){
25937         // if it doesn't have one, look for one
25938         if(!this.ownerTree){
25939             var p = this;
25940             while(p){
25941                 if(p.ownerTree){
25942                     this.ownerTree = p.ownerTree;
25943                     break;
25944                 }
25945                 p = p.parentNode;
25946             }
25947         }
25948         return this.ownerTree;
25949     },
25950
25951     /**
25952      * Returns depth of this node (the root node has a depth of 0)
25953      * @return {Number}
25954      */
25955     getDepth : function(){
25956         var depth = 0;
25957         var p = this;
25958         while(p.parentNode){
25959             ++depth;
25960             p = p.parentNode;
25961         }
25962         return depth;
25963     },
25964
25965     // private
25966     setOwnerTree : function(tree){
25967         // if it's move, we need to update everyone
25968         if(tree != this.ownerTree){
25969             if(this.ownerTree){
25970                 this.ownerTree.unregisterNode(this);
25971             }
25972             this.ownerTree = tree;
25973             var cs = this.childNodes;
25974             for(var i = 0, len = cs.length; i < len; i++) {
25975                 cs[i].setOwnerTree(tree);
25976             }
25977             if(tree){
25978                 tree.registerNode(this);
25979             }
25980         }
25981     },
25982
25983     /**
25984      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25985      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25986      * @return {String} The path
25987      */
25988     getPath : function(attr){
25989         attr = attr || "id";
25990         var p = this.parentNode;
25991         var b = [this.attributes[attr]];
25992         while(p){
25993             b.unshift(p.attributes[attr]);
25994             p = p.parentNode;
25995         }
25996         var sep = this.getOwnerTree().pathSeparator;
25997         return sep + b.join(sep);
25998     },
25999
26000     /**
26001      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26002      * function call will be the scope provided or the current node. The arguments to the function
26003      * will be the args provided or the current node. If the function returns false at any point,
26004      * the bubble is stopped.
26005      * @param {Function} fn The function to call
26006      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26007      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26008      */
26009     bubble : function(fn, scope, args){
26010         var p = this;
26011         while(p){
26012             if(fn.call(scope || p, args || p) === false){
26013                 break;
26014             }
26015             p = p.parentNode;
26016         }
26017     },
26018
26019     /**
26020      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26021      * function call will be the scope provided or the current node. The arguments to the function
26022      * will be the args provided or the current node. If the function returns false at any point,
26023      * the cascade is stopped on that branch.
26024      * @param {Function} fn The function to call
26025      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26026      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26027      */
26028     cascade : function(fn, scope, args){
26029         if(fn.call(scope || this, args || this) !== false){
26030             var cs = this.childNodes;
26031             for(var i = 0, len = cs.length; i < len; i++) {
26032                 cs[i].cascade(fn, scope, args);
26033             }
26034         }
26035     },
26036
26037     /**
26038      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26039      * function call will be the scope provided or the current node. The arguments to the function
26040      * will be the args provided or the current node. If the function returns false at any point,
26041      * the iteration stops.
26042      * @param {Function} fn The function to call
26043      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26044      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26045      */
26046     eachChild : function(fn, scope, args){
26047         var cs = this.childNodes;
26048         for(var i = 0, len = cs.length; i < len; i++) {
26049                 if(fn.call(scope || this, args || cs[i]) === false){
26050                     break;
26051                 }
26052         }
26053     },
26054
26055     /**
26056      * Finds the first child that has the attribute with the specified value.
26057      * @param {String} attribute The attribute name
26058      * @param {Mixed} value The value to search for
26059      * @return {Node} The found child or null if none was found
26060      */
26061     findChild : function(attribute, value){
26062         var cs = this.childNodes;
26063         for(var i = 0, len = cs.length; i < len; i++) {
26064                 if(cs[i].attributes[attribute] == value){
26065                     return cs[i];
26066                 }
26067         }
26068         return null;
26069     },
26070
26071     /**
26072      * Finds the first child by a custom function. The child matches if the function passed
26073      * returns true.
26074      * @param {Function} fn
26075      * @param {Object} scope (optional)
26076      * @return {Node} The found child or null if none was found
26077      */
26078     findChildBy : function(fn, scope){
26079         var cs = this.childNodes;
26080         for(var i = 0, len = cs.length; i < len; i++) {
26081                 if(fn.call(scope||cs[i], cs[i]) === true){
26082                     return cs[i];
26083                 }
26084         }
26085         return null;
26086     },
26087
26088     /**
26089      * Sorts this nodes children using the supplied sort function
26090      * @param {Function} fn
26091      * @param {Object} scope (optional)
26092      */
26093     sort : function(fn, scope){
26094         var cs = this.childNodes;
26095         var len = cs.length;
26096         if(len > 0){
26097             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26098             cs.sort(sortFn);
26099             for(var i = 0; i < len; i++){
26100                 var n = cs[i];
26101                 n.previousSibling = cs[i-1];
26102                 n.nextSibling = cs[i+1];
26103                 if(i == 0){
26104                     this.setFirstChild(n);
26105                 }
26106                 if(i == len-1){
26107                     this.setLastChild(n);
26108                 }
26109             }
26110         }
26111     },
26112
26113     /**
26114      * Returns true if this node is an ancestor (at any point) of the passed node.
26115      * @param {Node} node
26116      * @return {Boolean}
26117      */
26118     contains : function(node){
26119         return node.isAncestor(this);
26120     },
26121
26122     /**
26123      * Returns true if the passed node is an ancestor (at any point) of this node.
26124      * @param {Node} node
26125      * @return {Boolean}
26126      */
26127     isAncestor : function(node){
26128         var p = this.parentNode;
26129         while(p){
26130             if(p == node){
26131                 return true;
26132             }
26133             p = p.parentNode;
26134         }
26135         return false;
26136     },
26137
26138     toString : function(){
26139         return "[Node"+(this.id?" "+this.id:"")+"]";
26140     }
26141 });/*
26142  * Based on:
26143  * Ext JS Library 1.1.1
26144  * Copyright(c) 2006-2007, Ext JS, LLC.
26145  *
26146  * Originally Released Under LGPL - original licence link has changed is not relivant.
26147  *
26148  * Fork - LGPL
26149  * <script type="text/javascript">
26150  */
26151
26152
26153 /**
26154  * @class Roo.Shadow
26155  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26156  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26157  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26158  * @constructor
26159  * Create a new Shadow
26160  * @param {Object} config The config object
26161  */
26162 Roo.Shadow = function(config){
26163     Roo.apply(this, config);
26164     if(typeof this.mode != "string"){
26165         this.mode = this.defaultMode;
26166     }
26167     var o = this.offset, a = {h: 0};
26168     var rad = Math.floor(this.offset/2);
26169     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26170         case "drop":
26171             a.w = 0;
26172             a.l = a.t = o;
26173             a.t -= 1;
26174             if(Roo.isIE){
26175                 a.l -= this.offset + rad;
26176                 a.t -= this.offset + rad;
26177                 a.w -= rad;
26178                 a.h -= rad;
26179                 a.t += 1;
26180             }
26181         break;
26182         case "sides":
26183             a.w = (o*2);
26184             a.l = -o;
26185             a.t = o-1;
26186             if(Roo.isIE){
26187                 a.l -= (this.offset - rad);
26188                 a.t -= this.offset + rad;
26189                 a.l += 1;
26190                 a.w -= (this.offset - rad)*2;
26191                 a.w -= rad + 1;
26192                 a.h -= 1;
26193             }
26194         break;
26195         case "frame":
26196             a.w = a.h = (o*2);
26197             a.l = a.t = -o;
26198             a.t += 1;
26199             a.h -= 2;
26200             if(Roo.isIE){
26201                 a.l -= (this.offset - rad);
26202                 a.t -= (this.offset - rad);
26203                 a.l += 1;
26204                 a.w -= (this.offset + rad + 1);
26205                 a.h -= (this.offset + rad);
26206                 a.h += 1;
26207             }
26208         break;
26209     };
26210
26211     this.adjusts = a;
26212 };
26213
26214 Roo.Shadow.prototype = {
26215     /**
26216      * @cfg {String} mode
26217      * The shadow display mode.  Supports the following options:<br />
26218      * sides: Shadow displays on both sides and bottom only<br />
26219      * frame: Shadow displays equally on all four sides<br />
26220      * drop: Traditional bottom-right drop shadow (default)
26221      */
26222     mode: false,
26223     /**
26224      * @cfg {String} offset
26225      * The number of pixels to offset the shadow from the element (defaults to 4)
26226      */
26227     offset: 4,
26228
26229     // private
26230     defaultMode: "drop",
26231
26232     /**
26233      * Displays the shadow under the target element
26234      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26235      */
26236     show : function(target){
26237         target = Roo.get(target);
26238         if(!this.el){
26239             this.el = Roo.Shadow.Pool.pull();
26240             if(this.el.dom.nextSibling != target.dom){
26241                 this.el.insertBefore(target);
26242             }
26243         }
26244         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26245         if(Roo.isIE){
26246             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26247         }
26248         this.realign(
26249             target.getLeft(true),
26250             target.getTop(true),
26251             target.getWidth(),
26252             target.getHeight()
26253         );
26254         this.el.dom.style.display = "block";
26255     },
26256
26257     /**
26258      * Returns true if the shadow is visible, else false
26259      */
26260     isVisible : function(){
26261         return this.el ? true : false;  
26262     },
26263
26264     /**
26265      * Direct alignment when values are already available. Show must be called at least once before
26266      * calling this method to ensure it is initialized.
26267      * @param {Number} left The target element left position
26268      * @param {Number} top The target element top position
26269      * @param {Number} width The target element width
26270      * @param {Number} height The target element height
26271      */
26272     realign : function(l, t, w, h){
26273         if(!this.el){
26274             return;
26275         }
26276         var a = this.adjusts, d = this.el.dom, s = d.style;
26277         var iea = 0;
26278         s.left = (l+a.l)+"px";
26279         s.top = (t+a.t)+"px";
26280         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26281  
26282         if(s.width != sws || s.height != shs){
26283             s.width = sws;
26284             s.height = shs;
26285             if(!Roo.isIE){
26286                 var cn = d.childNodes;
26287                 var sww = Math.max(0, (sw-12))+"px";
26288                 cn[0].childNodes[1].style.width = sww;
26289                 cn[1].childNodes[1].style.width = sww;
26290                 cn[2].childNodes[1].style.width = sww;
26291                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26292             }
26293         }
26294     },
26295
26296     /**
26297      * Hides this shadow
26298      */
26299     hide : function(){
26300         if(this.el){
26301             this.el.dom.style.display = "none";
26302             Roo.Shadow.Pool.push(this.el);
26303             delete this.el;
26304         }
26305     },
26306
26307     /**
26308      * Adjust the z-index of this shadow
26309      * @param {Number} zindex The new z-index
26310      */
26311     setZIndex : function(z){
26312         this.zIndex = z;
26313         if(this.el){
26314             this.el.setStyle("z-index", z);
26315         }
26316     }
26317 };
26318
26319 // Private utility class that manages the internal Shadow cache
26320 Roo.Shadow.Pool = function(){
26321     var p = [];
26322     var markup = Roo.isIE ?
26323                  '<div class="x-ie-shadow"></div>' :
26324                  '<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>';
26325     return {
26326         pull : function(){
26327             var sh = p.shift();
26328             if(!sh){
26329                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26330                 sh.autoBoxAdjust = false;
26331             }
26332             return sh;
26333         },
26334
26335         push : function(sh){
26336             p.push(sh);
26337         }
26338     };
26339 }();/*
26340  * Based on:
26341  * Ext JS Library 1.1.1
26342  * Copyright(c) 2006-2007, Ext JS, LLC.
26343  *
26344  * Originally Released Under LGPL - original licence link has changed is not relivant.
26345  *
26346  * Fork - LGPL
26347  * <script type="text/javascript">
26348  */
26349
26350
26351 /**
26352  * @class Roo.SplitBar
26353  * @extends Roo.util.Observable
26354  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26355  * <br><br>
26356  * Usage:
26357  * <pre><code>
26358 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26359                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26360 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26361 split.minSize = 100;
26362 split.maxSize = 600;
26363 split.animate = true;
26364 split.on('moved', splitterMoved);
26365 </code></pre>
26366  * @constructor
26367  * Create a new SplitBar
26368  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26369  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26370  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26371  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26372                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26373                         position of the SplitBar).
26374  */
26375 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26376     
26377     /** @private */
26378     this.el = Roo.get(dragElement, true);
26379     this.el.dom.unselectable = "on";
26380     /** @private */
26381     this.resizingEl = Roo.get(resizingElement, true);
26382
26383     /**
26384      * @private
26385      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26386      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26387      * @type Number
26388      */
26389     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26390     
26391     /**
26392      * The minimum size of the resizing element. (Defaults to 0)
26393      * @type Number
26394      */
26395     this.minSize = 0;
26396     
26397     /**
26398      * The maximum size of the resizing element. (Defaults to 2000)
26399      * @type Number
26400      */
26401     this.maxSize = 2000;
26402     
26403     /**
26404      * Whether to animate the transition to the new size
26405      * @type Boolean
26406      */
26407     this.animate = false;
26408     
26409     /**
26410      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26411      * @type Boolean
26412      */
26413     this.useShim = false;
26414     
26415     /** @private */
26416     this.shim = null;
26417     
26418     if(!existingProxy){
26419         /** @private */
26420         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26421     }else{
26422         this.proxy = Roo.get(existingProxy).dom;
26423     }
26424     /** @private */
26425     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26426     
26427     /** @private */
26428     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26429     
26430     /** @private */
26431     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26432     
26433     /** @private */
26434     this.dragSpecs = {};
26435     
26436     /**
26437      * @private The adapter to use to positon and resize elements
26438      */
26439     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26440     this.adapter.init(this);
26441     
26442     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26443         /** @private */
26444         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26445         this.el.addClass("x-splitbar-h");
26446     }else{
26447         /** @private */
26448         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26449         this.el.addClass("x-splitbar-v");
26450     }
26451     
26452     this.addEvents({
26453         /**
26454          * @event resize
26455          * Fires when the splitter is moved (alias for {@link #event-moved})
26456          * @param {Roo.SplitBar} this
26457          * @param {Number} newSize the new width or height
26458          */
26459         "resize" : true,
26460         /**
26461          * @event moved
26462          * Fires when the splitter is moved
26463          * @param {Roo.SplitBar} this
26464          * @param {Number} newSize the new width or height
26465          */
26466         "moved" : true,
26467         /**
26468          * @event beforeresize
26469          * Fires before the splitter is dragged
26470          * @param {Roo.SplitBar} this
26471          */
26472         "beforeresize" : true,
26473
26474         "beforeapply" : true
26475     });
26476
26477     Roo.util.Observable.call(this);
26478 };
26479
26480 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26481     onStartProxyDrag : function(x, y){
26482         this.fireEvent("beforeresize", this);
26483         if(!this.overlay){
26484             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26485             o.unselectable();
26486             o.enableDisplayMode("block");
26487             // all splitbars share the same overlay
26488             Roo.SplitBar.prototype.overlay = o;
26489         }
26490         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26491         this.overlay.show();
26492         Roo.get(this.proxy).setDisplayed("block");
26493         var size = this.adapter.getElementSize(this);
26494         this.activeMinSize = this.getMinimumSize();;
26495         this.activeMaxSize = this.getMaximumSize();;
26496         var c1 = size - this.activeMinSize;
26497         var c2 = Math.max(this.activeMaxSize - size, 0);
26498         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26499             this.dd.resetConstraints();
26500             this.dd.setXConstraint(
26501                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26502                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26503             );
26504             this.dd.setYConstraint(0, 0);
26505         }else{
26506             this.dd.resetConstraints();
26507             this.dd.setXConstraint(0, 0);
26508             this.dd.setYConstraint(
26509                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26510                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26511             );
26512          }
26513         this.dragSpecs.startSize = size;
26514         this.dragSpecs.startPoint = [x, y];
26515         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26516     },
26517     
26518     /** 
26519      * @private Called after the drag operation by the DDProxy
26520      */
26521     onEndProxyDrag : function(e){
26522         Roo.get(this.proxy).setDisplayed(false);
26523         var endPoint = Roo.lib.Event.getXY(e);
26524         if(this.overlay){
26525             this.overlay.hide();
26526         }
26527         var newSize;
26528         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26529             newSize = this.dragSpecs.startSize + 
26530                 (this.placement == Roo.SplitBar.LEFT ?
26531                     endPoint[0] - this.dragSpecs.startPoint[0] :
26532                     this.dragSpecs.startPoint[0] - endPoint[0]
26533                 );
26534         }else{
26535             newSize = this.dragSpecs.startSize + 
26536                 (this.placement == Roo.SplitBar.TOP ?
26537                     endPoint[1] - this.dragSpecs.startPoint[1] :
26538                     this.dragSpecs.startPoint[1] - endPoint[1]
26539                 );
26540         }
26541         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26542         if(newSize != this.dragSpecs.startSize){
26543             if(this.fireEvent('beforeapply', this, newSize) !== false){
26544                 this.adapter.setElementSize(this, newSize);
26545                 this.fireEvent("moved", this, newSize);
26546                 this.fireEvent("resize", this, newSize);
26547             }
26548         }
26549     },
26550     
26551     /**
26552      * Get the adapter this SplitBar uses
26553      * @return The adapter object
26554      */
26555     getAdapter : function(){
26556         return this.adapter;
26557     },
26558     
26559     /**
26560      * Set the adapter this SplitBar uses
26561      * @param {Object} adapter A SplitBar adapter object
26562      */
26563     setAdapter : function(adapter){
26564         this.adapter = adapter;
26565         this.adapter.init(this);
26566     },
26567     
26568     /**
26569      * Gets the minimum size for the resizing element
26570      * @return {Number} The minimum size
26571      */
26572     getMinimumSize : function(){
26573         return this.minSize;
26574     },
26575     
26576     /**
26577      * Sets the minimum size for the resizing element
26578      * @param {Number} minSize The minimum size
26579      */
26580     setMinimumSize : function(minSize){
26581         this.minSize = minSize;
26582     },
26583     
26584     /**
26585      * Gets the maximum size for the resizing element
26586      * @return {Number} The maximum size
26587      */
26588     getMaximumSize : function(){
26589         return this.maxSize;
26590     },
26591     
26592     /**
26593      * Sets the maximum size for the resizing element
26594      * @param {Number} maxSize The maximum size
26595      */
26596     setMaximumSize : function(maxSize){
26597         this.maxSize = maxSize;
26598     },
26599     
26600     /**
26601      * Sets the initialize size for the resizing element
26602      * @param {Number} size The initial size
26603      */
26604     setCurrentSize : function(size){
26605         var oldAnimate = this.animate;
26606         this.animate = false;
26607         this.adapter.setElementSize(this, size);
26608         this.animate = oldAnimate;
26609     },
26610     
26611     /**
26612      * Destroy this splitbar. 
26613      * @param {Boolean} removeEl True to remove the element
26614      */
26615     destroy : function(removeEl){
26616         if(this.shim){
26617             this.shim.remove();
26618         }
26619         this.dd.unreg();
26620         this.proxy.parentNode.removeChild(this.proxy);
26621         if(removeEl){
26622             this.el.remove();
26623         }
26624     }
26625 });
26626
26627 /**
26628  * @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.
26629  */
26630 Roo.SplitBar.createProxy = function(dir){
26631     var proxy = new Roo.Element(document.createElement("div"));
26632     proxy.unselectable();
26633     var cls = 'x-splitbar-proxy';
26634     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26635     document.body.appendChild(proxy.dom);
26636     return proxy.dom;
26637 };
26638
26639 /** 
26640  * @class Roo.SplitBar.BasicLayoutAdapter
26641  * Default Adapter. It assumes the splitter and resizing element are not positioned
26642  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26643  */
26644 Roo.SplitBar.BasicLayoutAdapter = function(){
26645 };
26646
26647 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26648     // do nothing for now
26649     init : function(s){
26650     
26651     },
26652     /**
26653      * Called before drag operations to get the current size of the resizing element. 
26654      * @param {Roo.SplitBar} s The SplitBar using this adapter
26655      */
26656      getElementSize : function(s){
26657         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26658             return s.resizingEl.getWidth();
26659         }else{
26660             return s.resizingEl.getHeight();
26661         }
26662     },
26663     
26664     /**
26665      * Called after drag operations to set the size of the resizing element.
26666      * @param {Roo.SplitBar} s The SplitBar using this adapter
26667      * @param {Number} newSize The new size to set
26668      * @param {Function} onComplete A function to be invoked when resizing is complete
26669      */
26670     setElementSize : function(s, newSize, onComplete){
26671         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26672             if(!s.animate){
26673                 s.resizingEl.setWidth(newSize);
26674                 if(onComplete){
26675                     onComplete(s, newSize);
26676                 }
26677             }else{
26678                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26679             }
26680         }else{
26681             
26682             if(!s.animate){
26683                 s.resizingEl.setHeight(newSize);
26684                 if(onComplete){
26685                     onComplete(s, newSize);
26686                 }
26687             }else{
26688                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26689             }
26690         }
26691     }
26692 };
26693
26694 /** 
26695  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26696  * @extends Roo.SplitBar.BasicLayoutAdapter
26697  * Adapter that  moves the splitter element to align with the resized sizing element. 
26698  * Used with an absolute positioned SplitBar.
26699  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26700  * document.body, make sure you assign an id to the body element.
26701  */
26702 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26703     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26704     this.container = Roo.get(container);
26705 };
26706
26707 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26708     init : function(s){
26709         this.basic.init(s);
26710     },
26711     
26712     getElementSize : function(s){
26713         return this.basic.getElementSize(s);
26714     },
26715     
26716     setElementSize : function(s, newSize, onComplete){
26717         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26718     },
26719     
26720     moveSplitter : function(s){
26721         var yes = Roo.SplitBar;
26722         switch(s.placement){
26723             case yes.LEFT:
26724                 s.el.setX(s.resizingEl.getRight());
26725                 break;
26726             case yes.RIGHT:
26727                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26728                 break;
26729             case yes.TOP:
26730                 s.el.setY(s.resizingEl.getBottom());
26731                 break;
26732             case yes.BOTTOM:
26733                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26734                 break;
26735         }
26736     }
26737 };
26738
26739 /**
26740  * Orientation constant - Create a vertical SplitBar
26741  * @static
26742  * @type Number
26743  */
26744 Roo.SplitBar.VERTICAL = 1;
26745
26746 /**
26747  * Orientation constant - Create a horizontal SplitBar
26748  * @static
26749  * @type Number
26750  */
26751 Roo.SplitBar.HORIZONTAL = 2;
26752
26753 /**
26754  * Placement constant - The resizing element is to the left of the splitter element
26755  * @static
26756  * @type Number
26757  */
26758 Roo.SplitBar.LEFT = 1;
26759
26760 /**
26761  * Placement constant - The resizing element is to the right of the splitter element
26762  * @static
26763  * @type Number
26764  */
26765 Roo.SplitBar.RIGHT = 2;
26766
26767 /**
26768  * Placement constant - The resizing element is positioned above the splitter element
26769  * @static
26770  * @type Number
26771  */
26772 Roo.SplitBar.TOP = 3;
26773
26774 /**
26775  * Placement constant - The resizing element is positioned under splitter element
26776  * @static
26777  * @type Number
26778  */
26779 Roo.SplitBar.BOTTOM = 4;
26780 /*
26781  * Based on:
26782  * Ext JS Library 1.1.1
26783  * Copyright(c) 2006-2007, Ext JS, LLC.
26784  *
26785  * Originally Released Under LGPL - original licence link has changed is not relivant.
26786  *
26787  * Fork - LGPL
26788  * <script type="text/javascript">
26789  */
26790
26791 /**
26792  * @class Roo.View
26793  * @extends Roo.util.Observable
26794  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26795  * This class also supports single and multi selection modes. <br>
26796  * Create a data model bound view:
26797  <pre><code>
26798  var store = new Roo.data.Store(...);
26799
26800  var view = new Roo.View({
26801     el : "my-element",
26802     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26803  
26804     singleSelect: true,
26805     selectedClass: "ydataview-selected",
26806     store: store
26807  });
26808
26809  // listen for node click?
26810  view.on("click", function(vw, index, node, e){
26811  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26812  });
26813
26814  // load XML data
26815  dataModel.load("foobar.xml");
26816  </code></pre>
26817  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26818  * <br><br>
26819  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26820  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26821  * 
26822  * Note: old style constructor is still suported (container, template, config)
26823  * 
26824  * @constructor
26825  * Create a new View
26826  * @param {Object} config The config object
26827  * 
26828  */
26829 Roo.View = function(config, depreciated_tpl, depreciated_config){
26830     
26831     this.parent = false;
26832     
26833     if (typeof(depreciated_tpl) == 'undefined') {
26834         // new way.. - universal constructor.
26835         Roo.apply(this, config);
26836         this.el  = Roo.get(this.el);
26837     } else {
26838         // old format..
26839         this.el  = Roo.get(config);
26840         this.tpl = depreciated_tpl;
26841         Roo.apply(this, depreciated_config);
26842     }
26843     this.wrapEl  = this.el.wrap().wrap();
26844     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26845     
26846     
26847     if(typeof(this.tpl) == "string"){
26848         this.tpl = new Roo.Template(this.tpl);
26849     } else {
26850         // support xtype ctors..
26851         this.tpl = new Roo.factory(this.tpl, Roo);
26852     }
26853     
26854     
26855     this.tpl.compile();
26856     
26857     /** @private */
26858     this.addEvents({
26859         /**
26860          * @event beforeclick
26861          * Fires before a click is processed. Returns false to cancel the default action.
26862          * @param {Roo.View} this
26863          * @param {Number} index The index of the target node
26864          * @param {HTMLElement} node The target node
26865          * @param {Roo.EventObject} e The raw event object
26866          */
26867             "beforeclick" : true,
26868         /**
26869          * @event click
26870          * Fires when a template node is clicked.
26871          * @param {Roo.View} this
26872          * @param {Number} index The index of the target node
26873          * @param {HTMLElement} node The target node
26874          * @param {Roo.EventObject} e The raw event object
26875          */
26876             "click" : true,
26877         /**
26878          * @event dblclick
26879          * Fires when a template node is double clicked.
26880          * @param {Roo.View} this
26881          * @param {Number} index The index of the target node
26882          * @param {HTMLElement} node The target node
26883          * @param {Roo.EventObject} e The raw event object
26884          */
26885             "dblclick" : true,
26886         /**
26887          * @event contextmenu
26888          * Fires when a template node is right clicked.
26889          * @param {Roo.View} this
26890          * @param {Number} index The index of the target node
26891          * @param {HTMLElement} node The target node
26892          * @param {Roo.EventObject} e The raw event object
26893          */
26894             "contextmenu" : true,
26895         /**
26896          * @event selectionchange
26897          * Fires when the selected nodes change.
26898          * @param {Roo.View} this
26899          * @param {Array} selections Array of the selected nodes
26900          */
26901             "selectionchange" : true,
26902     
26903         /**
26904          * @event beforeselect
26905          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26906          * @param {Roo.View} this
26907          * @param {HTMLElement} node The node to be selected
26908          * @param {Array} selections Array of currently selected nodes
26909          */
26910             "beforeselect" : true,
26911         /**
26912          * @event preparedata
26913          * Fires on every row to render, to allow you to change the data.
26914          * @param {Roo.View} this
26915          * @param {Object} data to be rendered (change this)
26916          */
26917           "preparedata" : true
26918           
26919           
26920         });
26921
26922
26923
26924     this.el.on({
26925         "click": this.onClick,
26926         "dblclick": this.onDblClick,
26927         "contextmenu": this.onContextMenu,
26928         scope:this
26929     });
26930
26931     this.selections = [];
26932     this.nodes = [];
26933     this.cmp = new Roo.CompositeElementLite([]);
26934     if(this.store){
26935         this.store = Roo.factory(this.store, Roo.data);
26936         this.setStore(this.store, true);
26937     }
26938     
26939     if ( this.footer && this.footer.xtype) {
26940            
26941          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26942         
26943         this.footer.dataSource = this.store;
26944         this.footer.container = fctr;
26945         this.footer = Roo.factory(this.footer, Roo);
26946         fctr.insertFirst(this.el);
26947         
26948         // this is a bit insane - as the paging toolbar seems to detach the el..
26949 //        dom.parentNode.parentNode.parentNode
26950          // they get detached?
26951     }
26952     
26953     
26954     Roo.View.superclass.constructor.call(this);
26955     
26956     
26957 };
26958
26959 Roo.extend(Roo.View, Roo.util.Observable, {
26960     
26961      /**
26962      * @cfg {Roo.data.Store} store Data store to load data from.
26963      */
26964     store : false,
26965     
26966     /**
26967      * @cfg {String|Roo.Element} el The container element.
26968      */
26969     el : '',
26970     
26971     /**
26972      * @cfg {String|Roo.Template} tpl The template used by this View 
26973      */
26974     tpl : false,
26975     /**
26976      * @cfg {String} dataName the named area of the template to use as the data area
26977      *                          Works with domtemplates roo-name="name"
26978      */
26979     dataName: false,
26980     /**
26981      * @cfg {String} selectedClass The css class to add to selected nodes
26982      */
26983     selectedClass : "x-view-selected",
26984      /**
26985      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26986      */
26987     emptyText : "",
26988     
26989     /**
26990      * @cfg {String} text to display on mask (default Loading)
26991      */
26992     mask : false,
26993     /**
26994      * @cfg {Boolean} multiSelect Allow multiple selection
26995      */
26996     multiSelect : false,
26997     /**
26998      * @cfg {Boolean} singleSelect Allow single selection
26999      */
27000     singleSelect:  false,
27001     
27002     /**
27003      * @cfg {Boolean} toggleSelect - selecting 
27004      */
27005     toggleSelect : false,
27006     
27007     /**
27008      * @cfg {Boolean} tickable - selecting 
27009      */
27010     tickable : false,
27011     
27012     /**
27013      * Returns the element this view is bound to.
27014      * @return {Roo.Element}
27015      */
27016     getEl : function(){
27017         return this.wrapEl;
27018     },
27019     
27020     
27021
27022     /**
27023      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27024      */
27025     refresh : function(){
27026         //Roo.log('refresh');
27027         var t = this.tpl;
27028         
27029         // if we are using something like 'domtemplate', then
27030         // the what gets used is:
27031         // t.applySubtemplate(NAME, data, wrapping data..)
27032         // the outer template then get' applied with
27033         //     the store 'extra data'
27034         // and the body get's added to the
27035         //      roo-name="data" node?
27036         //      <span class='roo-tpl-{name}'></span> ?????
27037         
27038         
27039         
27040         this.clearSelections();
27041         this.el.update("");
27042         var html = [];
27043         var records = this.store.getRange();
27044         if(records.length < 1) {
27045             
27046             // is this valid??  = should it render a template??
27047             
27048             this.el.update(this.emptyText);
27049             return;
27050         }
27051         var el = this.el;
27052         if (this.dataName) {
27053             this.el.update(t.apply(this.store.meta)); //????
27054             el = this.el.child('.roo-tpl-' + this.dataName);
27055         }
27056         
27057         for(var i = 0, len = records.length; i < len; i++){
27058             var data = this.prepareData(records[i].data, i, records[i]);
27059             this.fireEvent("preparedata", this, data, i, records[i]);
27060             
27061             var d = Roo.apply({}, data);
27062             
27063             if(this.tickable){
27064                 Roo.apply(d, {'roo-id' : Roo.id()});
27065                 
27066                 var _this = this;
27067             
27068                 Roo.each(this.parent.item, function(item){
27069                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27070                         return;
27071                     }
27072                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27073                 });
27074             }
27075             
27076             html[html.length] = Roo.util.Format.trim(
27077                 this.dataName ?
27078                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27079                     t.apply(d)
27080             );
27081         }
27082         
27083         
27084         
27085         el.update(html.join(""));
27086         this.nodes = el.dom.childNodes;
27087         this.updateIndexes(0);
27088     },
27089     
27090
27091     /**
27092      * Function to override to reformat the data that is sent to
27093      * the template for each node.
27094      * DEPRICATED - use the preparedata event handler.
27095      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27096      * a JSON object for an UpdateManager bound view).
27097      */
27098     prepareData : function(data, index, record)
27099     {
27100         this.fireEvent("preparedata", this, data, index, record);
27101         return data;
27102     },
27103
27104     onUpdate : function(ds, record){
27105         // Roo.log('on update');   
27106         this.clearSelections();
27107         var index = this.store.indexOf(record);
27108         var n = this.nodes[index];
27109         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27110         n.parentNode.removeChild(n);
27111         this.updateIndexes(index, index);
27112     },
27113
27114     
27115     
27116 // --------- FIXME     
27117     onAdd : function(ds, records, index)
27118     {
27119         //Roo.log(['on Add', ds, records, index] );        
27120         this.clearSelections();
27121         if(this.nodes.length == 0){
27122             this.refresh();
27123             return;
27124         }
27125         var n = this.nodes[index];
27126         for(var i = 0, len = records.length; i < len; i++){
27127             var d = this.prepareData(records[i].data, i, records[i]);
27128             if(n){
27129                 this.tpl.insertBefore(n, d);
27130             }else{
27131                 
27132                 this.tpl.append(this.el, d);
27133             }
27134         }
27135         this.updateIndexes(index);
27136     },
27137
27138     onRemove : function(ds, record, index){
27139        // Roo.log('onRemove');
27140         this.clearSelections();
27141         var el = this.dataName  ?
27142             this.el.child('.roo-tpl-' + this.dataName) :
27143             this.el; 
27144         
27145         el.dom.removeChild(this.nodes[index]);
27146         this.updateIndexes(index);
27147     },
27148
27149     /**
27150      * Refresh an individual node.
27151      * @param {Number} index
27152      */
27153     refreshNode : function(index){
27154         this.onUpdate(this.store, this.store.getAt(index));
27155     },
27156
27157     updateIndexes : function(startIndex, endIndex){
27158         var ns = this.nodes;
27159         startIndex = startIndex || 0;
27160         endIndex = endIndex || ns.length - 1;
27161         for(var i = startIndex; i <= endIndex; i++){
27162             ns[i].nodeIndex = i;
27163         }
27164     },
27165
27166     /**
27167      * Changes the data store this view uses and refresh the view.
27168      * @param {Store} store
27169      */
27170     setStore : function(store, initial){
27171         if(!initial && this.store){
27172             this.store.un("datachanged", this.refresh);
27173             this.store.un("add", this.onAdd);
27174             this.store.un("remove", this.onRemove);
27175             this.store.un("update", this.onUpdate);
27176             this.store.un("clear", this.refresh);
27177             this.store.un("beforeload", this.onBeforeLoad);
27178             this.store.un("load", this.onLoad);
27179             this.store.un("loadexception", this.onLoad);
27180         }
27181         if(store){
27182           
27183             store.on("datachanged", this.refresh, this);
27184             store.on("add", this.onAdd, this);
27185             store.on("remove", this.onRemove, this);
27186             store.on("update", this.onUpdate, this);
27187             store.on("clear", this.refresh, this);
27188             store.on("beforeload", this.onBeforeLoad, this);
27189             store.on("load", this.onLoad, this);
27190             store.on("loadexception", this.onLoad, this);
27191         }
27192         
27193         if(store){
27194             this.refresh();
27195         }
27196     },
27197     /**
27198      * onbeforeLoad - masks the loading area.
27199      *
27200      */
27201     onBeforeLoad : function(store,opts)
27202     {
27203          //Roo.log('onBeforeLoad');   
27204         if (!opts.add) {
27205             this.el.update("");
27206         }
27207         this.el.mask(this.mask ? this.mask : "Loading" ); 
27208     },
27209     onLoad : function ()
27210     {
27211         this.el.unmask();
27212     },
27213     
27214
27215     /**
27216      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27217      * @param {HTMLElement} node
27218      * @return {HTMLElement} The template node
27219      */
27220     findItemFromChild : function(node){
27221         var el = this.dataName  ?
27222             this.el.child('.roo-tpl-' + this.dataName,true) :
27223             this.el.dom; 
27224         
27225         if(!node || node.parentNode == el){
27226                     return node;
27227             }
27228             var p = node.parentNode;
27229             while(p && p != el){
27230             if(p.parentNode == el){
27231                 return p;
27232             }
27233             p = p.parentNode;
27234         }
27235             return null;
27236     },
27237
27238     /** @ignore */
27239     onClick : function(e){
27240         var item = this.findItemFromChild(e.getTarget());
27241         if(item){
27242             var index = this.indexOf(item);
27243             if(this.onItemClick(item, index, e) !== false){
27244                 this.fireEvent("click", this, index, item, e);
27245             }
27246         }else{
27247             this.clearSelections();
27248         }
27249     },
27250
27251     /** @ignore */
27252     onContextMenu : function(e){
27253         var item = this.findItemFromChild(e.getTarget());
27254         if(item){
27255             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27256         }
27257     },
27258
27259     /** @ignore */
27260     onDblClick : function(e){
27261         var item = this.findItemFromChild(e.getTarget());
27262         if(item){
27263             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27264         }
27265     },
27266
27267     onItemClick : function(item, index, e)
27268     {
27269         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27270             return false;
27271         }
27272         if (this.toggleSelect) {
27273             var m = this.isSelected(item) ? 'unselect' : 'select';
27274             //Roo.log(m);
27275             var _t = this;
27276             _t[m](item, true, false);
27277             return true;
27278         }
27279         if(this.multiSelect || this.singleSelect){
27280             if(this.multiSelect && e.shiftKey && this.lastSelection){
27281                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27282             }else{
27283                 this.select(item, this.multiSelect && e.ctrlKey);
27284                 this.lastSelection = item;
27285             }
27286             
27287             if(!this.tickable){
27288                 e.preventDefault();
27289             }
27290             
27291         }
27292         return true;
27293     },
27294
27295     /**
27296      * Get the number of selected nodes.
27297      * @return {Number}
27298      */
27299     getSelectionCount : function(){
27300         return this.selections.length;
27301     },
27302
27303     /**
27304      * Get the currently selected nodes.
27305      * @return {Array} An array of HTMLElements
27306      */
27307     getSelectedNodes : function(){
27308         return this.selections;
27309     },
27310
27311     /**
27312      * Get the indexes of the selected nodes.
27313      * @return {Array}
27314      */
27315     getSelectedIndexes : function(){
27316         var indexes = [], s = this.selections;
27317         for(var i = 0, len = s.length; i < len; i++){
27318             indexes.push(s[i].nodeIndex);
27319         }
27320         return indexes;
27321     },
27322
27323     /**
27324      * Clear all selections
27325      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27326      */
27327     clearSelections : function(suppressEvent){
27328         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27329             this.cmp.elements = this.selections;
27330             this.cmp.removeClass(this.selectedClass);
27331             this.selections = [];
27332             if(!suppressEvent){
27333                 this.fireEvent("selectionchange", this, this.selections);
27334             }
27335         }
27336     },
27337
27338     /**
27339      * Returns true if the passed node is selected
27340      * @param {HTMLElement/Number} node The node or node index
27341      * @return {Boolean}
27342      */
27343     isSelected : function(node){
27344         var s = this.selections;
27345         if(s.length < 1){
27346             return false;
27347         }
27348         node = this.getNode(node);
27349         return s.indexOf(node) !== -1;
27350     },
27351
27352     /**
27353      * Selects nodes.
27354      * @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
27355      * @param {Boolean} keepExisting (optional) true to keep existing selections
27356      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27357      */
27358     select : function(nodeInfo, keepExisting, suppressEvent){
27359         if(nodeInfo instanceof Array){
27360             if(!keepExisting){
27361                 this.clearSelections(true);
27362             }
27363             for(var i = 0, len = nodeInfo.length; i < len; i++){
27364                 this.select(nodeInfo[i], true, true);
27365             }
27366             return;
27367         } 
27368         var node = this.getNode(nodeInfo);
27369         if(!node || this.isSelected(node)){
27370             return; // already selected.
27371         }
27372         if(!keepExisting){
27373             this.clearSelections(true);
27374         }
27375         
27376         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27377             Roo.fly(node).addClass(this.selectedClass);
27378             this.selections.push(node);
27379             if(!suppressEvent){
27380                 this.fireEvent("selectionchange", this, this.selections);
27381             }
27382         }
27383         
27384         
27385     },
27386       /**
27387      * Unselects nodes.
27388      * @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
27389      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27391      */
27392     unselect : function(nodeInfo, keepExisting, suppressEvent)
27393     {
27394         if(nodeInfo instanceof Array){
27395             Roo.each(this.selections, function(s) {
27396                 this.unselect(s, nodeInfo);
27397             }, this);
27398             return;
27399         }
27400         var node = this.getNode(nodeInfo);
27401         if(!node || !this.isSelected(node)){
27402             //Roo.log("not selected");
27403             return; // not selected.
27404         }
27405         // fireevent???
27406         var ns = [];
27407         Roo.each(this.selections, function(s) {
27408             if (s == node ) {
27409                 Roo.fly(node).removeClass(this.selectedClass);
27410
27411                 return;
27412             }
27413             ns.push(s);
27414         },this);
27415         
27416         this.selections= ns;
27417         this.fireEvent("selectionchange", this, this.selections);
27418     },
27419
27420     /**
27421      * Gets a template node.
27422      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27423      * @return {HTMLElement} The node or null if it wasn't found
27424      */
27425     getNode : function(nodeInfo){
27426         if(typeof nodeInfo == "string"){
27427             return document.getElementById(nodeInfo);
27428         }else if(typeof nodeInfo == "number"){
27429             return this.nodes[nodeInfo];
27430         }
27431         return nodeInfo;
27432     },
27433
27434     /**
27435      * Gets a range template nodes.
27436      * @param {Number} startIndex
27437      * @param {Number} endIndex
27438      * @return {Array} An array of nodes
27439      */
27440     getNodes : function(start, end){
27441         var ns = this.nodes;
27442         start = start || 0;
27443         end = typeof end == "undefined" ? ns.length - 1 : end;
27444         var nodes = [];
27445         if(start <= end){
27446             for(var i = start; i <= end; i++){
27447                 nodes.push(ns[i]);
27448             }
27449         } else{
27450             for(var i = start; i >= end; i--){
27451                 nodes.push(ns[i]);
27452             }
27453         }
27454         return nodes;
27455     },
27456
27457     /**
27458      * Finds the index of the passed node
27459      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27460      * @return {Number} The index of the node or -1
27461      */
27462     indexOf : function(node){
27463         node = this.getNode(node);
27464         if(typeof node.nodeIndex == "number"){
27465             return node.nodeIndex;
27466         }
27467         var ns = this.nodes;
27468         for(var i = 0, len = ns.length; i < len; i++){
27469             if(ns[i] == node){
27470                 return i;
27471             }
27472         }
27473         return -1;
27474     }
27475 });
27476 /*
27477  * Based on:
27478  * Ext JS Library 1.1.1
27479  * Copyright(c) 2006-2007, Ext JS, LLC.
27480  *
27481  * Originally Released Under LGPL - original licence link has changed is not relivant.
27482  *
27483  * Fork - LGPL
27484  * <script type="text/javascript">
27485  */
27486
27487 /**
27488  * @class Roo.JsonView
27489  * @extends Roo.View
27490  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27491 <pre><code>
27492 var view = new Roo.JsonView({
27493     container: "my-element",
27494     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27495     multiSelect: true, 
27496     jsonRoot: "data" 
27497 });
27498
27499 // listen for node click?
27500 view.on("click", function(vw, index, node, e){
27501     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27502 });
27503
27504 // direct load of JSON data
27505 view.load("foobar.php");
27506
27507 // Example from my blog list
27508 var tpl = new Roo.Template(
27509     '&lt;div class="entry"&gt;' +
27510     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27511     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27512     "&lt;/div&gt;&lt;hr /&gt;"
27513 );
27514
27515 var moreView = new Roo.JsonView({
27516     container :  "entry-list", 
27517     template : tpl,
27518     jsonRoot: "posts"
27519 });
27520 moreView.on("beforerender", this.sortEntries, this);
27521 moreView.load({
27522     url: "/blog/get-posts.php",
27523     params: "allposts=true",
27524     text: "Loading Blog Entries..."
27525 });
27526 </code></pre>
27527
27528 * Note: old code is supported with arguments : (container, template, config)
27529
27530
27531  * @constructor
27532  * Create a new JsonView
27533  * 
27534  * @param {Object} config The config object
27535  * 
27536  */
27537 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27538     
27539     
27540     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27541
27542     var um = this.el.getUpdateManager();
27543     um.setRenderer(this);
27544     um.on("update", this.onLoad, this);
27545     um.on("failure", this.onLoadException, this);
27546
27547     /**
27548      * @event beforerender
27549      * Fires before rendering of the downloaded JSON data.
27550      * @param {Roo.JsonView} this
27551      * @param {Object} data The JSON data loaded
27552      */
27553     /**
27554      * @event load
27555      * Fires when data is loaded.
27556      * @param {Roo.JsonView} this
27557      * @param {Object} data The JSON data loaded
27558      * @param {Object} response The raw Connect response object
27559      */
27560     /**
27561      * @event loadexception
27562      * Fires when loading fails.
27563      * @param {Roo.JsonView} this
27564      * @param {Object} response The raw Connect response object
27565      */
27566     this.addEvents({
27567         'beforerender' : true,
27568         'load' : true,
27569         'loadexception' : true
27570     });
27571 };
27572 Roo.extend(Roo.JsonView, Roo.View, {
27573     /**
27574      * @type {String} The root property in the loaded JSON object that contains the data
27575      */
27576     jsonRoot : "",
27577
27578     /**
27579      * Refreshes the view.
27580      */
27581     refresh : function(){
27582         this.clearSelections();
27583         this.el.update("");
27584         var html = [];
27585         var o = this.jsonData;
27586         if(o && o.length > 0){
27587             for(var i = 0, len = o.length; i < len; i++){
27588                 var data = this.prepareData(o[i], i, o);
27589                 html[html.length] = this.tpl.apply(data);
27590             }
27591         }else{
27592             html.push(this.emptyText);
27593         }
27594         this.el.update(html.join(""));
27595         this.nodes = this.el.dom.childNodes;
27596         this.updateIndexes(0);
27597     },
27598
27599     /**
27600      * 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.
27601      * @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:
27602      <pre><code>
27603      view.load({
27604          url: "your-url.php",
27605          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27606          callback: yourFunction,
27607          scope: yourObject, //(optional scope)
27608          discardUrl: false,
27609          nocache: false,
27610          text: "Loading...",
27611          timeout: 30,
27612          scripts: false
27613      });
27614      </code></pre>
27615      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27616      * 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.
27617      * @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}
27618      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27619      * @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.
27620      */
27621     load : function(){
27622         var um = this.el.getUpdateManager();
27623         um.update.apply(um, arguments);
27624     },
27625
27626     // note - render is a standard framework call...
27627     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27628     render : function(el, response){
27629         
27630         this.clearSelections();
27631         this.el.update("");
27632         var o;
27633         try{
27634             if (response != '') {
27635                 o = Roo.util.JSON.decode(response.responseText);
27636                 if(this.jsonRoot){
27637                     
27638                     o = o[this.jsonRoot];
27639                 }
27640             }
27641         } catch(e){
27642         }
27643         /**
27644          * The current JSON data or null
27645          */
27646         this.jsonData = o;
27647         this.beforeRender();
27648         this.refresh();
27649     },
27650
27651 /**
27652  * Get the number of records in the current JSON dataset
27653  * @return {Number}
27654  */
27655     getCount : function(){
27656         return this.jsonData ? this.jsonData.length : 0;
27657     },
27658
27659 /**
27660  * Returns the JSON object for the specified node(s)
27661  * @param {HTMLElement/Array} node The node or an array of nodes
27662  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27663  * you get the JSON object for the node
27664  */
27665     getNodeData : function(node){
27666         if(node instanceof Array){
27667             var data = [];
27668             for(var i = 0, len = node.length; i < len; i++){
27669                 data.push(this.getNodeData(node[i]));
27670             }
27671             return data;
27672         }
27673         return this.jsonData[this.indexOf(node)] || null;
27674     },
27675
27676     beforeRender : function(){
27677         this.snapshot = this.jsonData;
27678         if(this.sortInfo){
27679             this.sort.apply(this, this.sortInfo);
27680         }
27681         this.fireEvent("beforerender", this, this.jsonData);
27682     },
27683
27684     onLoad : function(el, o){
27685         this.fireEvent("load", this, this.jsonData, o);
27686     },
27687
27688     onLoadException : function(el, o){
27689         this.fireEvent("loadexception", this, o);
27690     },
27691
27692 /**
27693  * Filter the data by a specific property.
27694  * @param {String} property A property on your JSON objects
27695  * @param {String/RegExp} value Either string that the property values
27696  * should start with, or a RegExp to test against the property
27697  */
27698     filter : function(property, value){
27699         if(this.jsonData){
27700             var data = [];
27701             var ss = this.snapshot;
27702             if(typeof value == "string"){
27703                 var vlen = value.length;
27704                 if(vlen == 0){
27705                     this.clearFilter();
27706                     return;
27707                 }
27708                 value = value.toLowerCase();
27709                 for(var i = 0, len = ss.length; i < len; i++){
27710                     var o = ss[i];
27711                     if(o[property].substr(0, vlen).toLowerCase() == value){
27712                         data.push(o);
27713                     }
27714                 }
27715             } else if(value.exec){ // regex?
27716                 for(var i = 0, len = ss.length; i < len; i++){
27717                     var o = ss[i];
27718                     if(value.test(o[property])){
27719                         data.push(o);
27720                     }
27721                 }
27722             } else{
27723                 return;
27724             }
27725             this.jsonData = data;
27726             this.refresh();
27727         }
27728     },
27729
27730 /**
27731  * Filter by a function. The passed function will be called with each
27732  * object in the current dataset. If the function returns true the value is kept,
27733  * otherwise it is filtered.
27734  * @param {Function} fn
27735  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27736  */
27737     filterBy : function(fn, scope){
27738         if(this.jsonData){
27739             var data = [];
27740             var ss = this.snapshot;
27741             for(var i = 0, len = ss.length; i < len; i++){
27742                 var o = ss[i];
27743                 if(fn.call(scope || this, o)){
27744                     data.push(o);
27745                 }
27746             }
27747             this.jsonData = data;
27748             this.refresh();
27749         }
27750     },
27751
27752 /**
27753  * Clears the current filter.
27754  */
27755     clearFilter : function(){
27756         if(this.snapshot && this.jsonData != this.snapshot){
27757             this.jsonData = this.snapshot;
27758             this.refresh();
27759         }
27760     },
27761
27762
27763 /**
27764  * Sorts the data for this view and refreshes it.
27765  * @param {String} property A property on your JSON objects to sort on
27766  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27767  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27768  */
27769     sort : function(property, dir, sortType){
27770         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27771         if(this.jsonData){
27772             var p = property;
27773             var dsc = dir && dir.toLowerCase() == "desc";
27774             var f = function(o1, o2){
27775                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27776                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27777                 ;
27778                 if(v1 < v2){
27779                     return dsc ? +1 : -1;
27780                 } else if(v1 > v2){
27781                     return dsc ? -1 : +1;
27782                 } else{
27783                     return 0;
27784                 }
27785             };
27786             this.jsonData.sort(f);
27787             this.refresh();
27788             if(this.jsonData != this.snapshot){
27789                 this.snapshot.sort(f);
27790             }
27791         }
27792     }
27793 });/*
27794  * Based on:
27795  * Ext JS Library 1.1.1
27796  * Copyright(c) 2006-2007, Ext JS, LLC.
27797  *
27798  * Originally Released Under LGPL - original licence link has changed is not relivant.
27799  *
27800  * Fork - LGPL
27801  * <script type="text/javascript">
27802  */
27803  
27804
27805 /**
27806  * @class Roo.ColorPalette
27807  * @extends Roo.Component
27808  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27809  * Here's an example of typical usage:
27810  * <pre><code>
27811 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27812 cp.render('my-div');
27813
27814 cp.on('select', function(palette, selColor){
27815     // do something with selColor
27816 });
27817 </code></pre>
27818  * @constructor
27819  * Create a new ColorPalette
27820  * @param {Object} config The config object
27821  */
27822 Roo.ColorPalette = function(config){
27823     Roo.ColorPalette.superclass.constructor.call(this, config);
27824     this.addEvents({
27825         /**
27826              * @event select
27827              * Fires when a color is selected
27828              * @param {ColorPalette} this
27829              * @param {String} color The 6-digit color hex code (without the # symbol)
27830              */
27831         select: true
27832     });
27833
27834     if(this.handler){
27835         this.on("select", this.handler, this.scope, true);
27836     }
27837 };
27838 Roo.extend(Roo.ColorPalette, Roo.Component, {
27839     /**
27840      * @cfg {String} itemCls
27841      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27842      */
27843     itemCls : "x-color-palette",
27844     /**
27845      * @cfg {String} value
27846      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27847      * the hex codes are case-sensitive.
27848      */
27849     value : null,
27850     clickEvent:'click',
27851     // private
27852     ctype: "Roo.ColorPalette",
27853
27854     /**
27855      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27856      */
27857     allowReselect : false,
27858
27859     /**
27860      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27861      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27862      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27863      * of colors with the width setting until the box is symmetrical.</p>
27864      * <p>You can override individual colors if needed:</p>
27865      * <pre><code>
27866 var cp = new Roo.ColorPalette();
27867 cp.colors[0] = "FF0000";  // change the first box to red
27868 </code></pre>
27869
27870 Or you can provide a custom array of your own for complete control:
27871 <pre><code>
27872 var cp = new Roo.ColorPalette();
27873 cp.colors = ["000000", "993300", "333300"];
27874 </code></pre>
27875      * @type Array
27876      */
27877     colors : [
27878         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27879         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27880         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27881         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27882         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27883     ],
27884
27885     // private
27886     onRender : function(container, position){
27887         var t = new Roo.MasterTemplate(
27888             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27889         );
27890         var c = this.colors;
27891         for(var i = 0, len = c.length; i < len; i++){
27892             t.add([c[i]]);
27893         }
27894         var el = document.createElement("div");
27895         el.className = this.itemCls;
27896         t.overwrite(el);
27897         container.dom.insertBefore(el, position);
27898         this.el = Roo.get(el);
27899         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27900         if(this.clickEvent != 'click'){
27901             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27902         }
27903     },
27904
27905     // private
27906     afterRender : function(){
27907         Roo.ColorPalette.superclass.afterRender.call(this);
27908         if(this.value){
27909             var s = this.value;
27910             this.value = null;
27911             this.select(s);
27912         }
27913     },
27914
27915     // private
27916     handleClick : function(e, t){
27917         e.preventDefault();
27918         if(!this.disabled){
27919             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27920             this.select(c.toUpperCase());
27921         }
27922     },
27923
27924     /**
27925      * Selects the specified color in the palette (fires the select event)
27926      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27927      */
27928     select : function(color){
27929         color = color.replace("#", "");
27930         if(color != this.value || this.allowReselect){
27931             var el = this.el;
27932             if(this.value){
27933                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27934             }
27935             el.child("a.color-"+color).addClass("x-color-palette-sel");
27936             this.value = color;
27937             this.fireEvent("select", this, color);
27938         }
27939     }
27940 });/*
27941  * Based on:
27942  * Ext JS Library 1.1.1
27943  * Copyright(c) 2006-2007, Ext JS, LLC.
27944  *
27945  * Originally Released Under LGPL - original licence link has changed is not relivant.
27946  *
27947  * Fork - LGPL
27948  * <script type="text/javascript">
27949  */
27950  
27951 /**
27952  * @class Roo.DatePicker
27953  * @extends Roo.Component
27954  * Simple date picker class.
27955  * @constructor
27956  * Create a new DatePicker
27957  * @param {Object} config The config object
27958  */
27959 Roo.DatePicker = function(config){
27960     Roo.DatePicker.superclass.constructor.call(this, config);
27961
27962     this.value = config && config.value ?
27963                  config.value.clearTime() : new Date().clearTime();
27964
27965     this.addEvents({
27966         /**
27967              * @event select
27968              * Fires when a date is selected
27969              * @param {DatePicker} this
27970              * @param {Date} date The selected date
27971              */
27972         'select': true,
27973         /**
27974              * @event monthchange
27975              * Fires when the displayed month changes 
27976              * @param {DatePicker} this
27977              * @param {Date} date The selected month
27978              */
27979         'monthchange': true
27980     });
27981
27982     if(this.handler){
27983         this.on("select", this.handler,  this.scope || this);
27984     }
27985     // build the disabledDatesRE
27986     if(!this.disabledDatesRE && this.disabledDates){
27987         var dd = this.disabledDates;
27988         var re = "(?:";
27989         for(var i = 0; i < dd.length; i++){
27990             re += dd[i];
27991             if(i != dd.length-1) {
27992                 re += "|";
27993             }
27994         }
27995         this.disabledDatesRE = new RegExp(re + ")");
27996     }
27997 };
27998
27999 Roo.extend(Roo.DatePicker, Roo.Component, {
28000     /**
28001      * @cfg {String} todayText
28002      * The text to display on the button that selects the current date (defaults to "Today")
28003      */
28004     todayText : "Today",
28005     /**
28006      * @cfg {String} okText
28007      * The text to display on the ok button
28008      */
28009     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28010     /**
28011      * @cfg {String} cancelText
28012      * The text to display on the cancel button
28013      */
28014     cancelText : "Cancel",
28015     /**
28016      * @cfg {String} todayTip
28017      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28018      */
28019     todayTip : "{0} (Spacebar)",
28020     /**
28021      * @cfg {Date} minDate
28022      * Minimum allowable date (JavaScript date object, defaults to null)
28023      */
28024     minDate : null,
28025     /**
28026      * @cfg {Date} maxDate
28027      * Maximum allowable date (JavaScript date object, defaults to null)
28028      */
28029     maxDate : null,
28030     /**
28031      * @cfg {String} minText
28032      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28033      */
28034     minText : "This date is before the minimum date",
28035     /**
28036      * @cfg {String} maxText
28037      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28038      */
28039     maxText : "This date is after the maximum date",
28040     /**
28041      * @cfg {String} format
28042      * The default date format string which can be overriden for localization support.  The format must be
28043      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28044      */
28045     format : "m/d/y",
28046     /**
28047      * @cfg {Array} disabledDays
28048      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28049      */
28050     disabledDays : null,
28051     /**
28052      * @cfg {String} disabledDaysText
28053      * The tooltip to display when the date falls on a disabled day (defaults to "")
28054      */
28055     disabledDaysText : "",
28056     /**
28057      * @cfg {RegExp} disabledDatesRE
28058      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28059      */
28060     disabledDatesRE : null,
28061     /**
28062      * @cfg {String} disabledDatesText
28063      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28064      */
28065     disabledDatesText : "",
28066     /**
28067      * @cfg {Boolean} constrainToViewport
28068      * True to constrain the date picker to the viewport (defaults to true)
28069      */
28070     constrainToViewport : true,
28071     /**
28072      * @cfg {Array} monthNames
28073      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28074      */
28075     monthNames : Date.monthNames,
28076     /**
28077      * @cfg {Array} dayNames
28078      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28079      */
28080     dayNames : Date.dayNames,
28081     /**
28082      * @cfg {String} nextText
28083      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28084      */
28085     nextText: 'Next Month (Control+Right)',
28086     /**
28087      * @cfg {String} prevText
28088      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28089      */
28090     prevText: 'Previous Month (Control+Left)',
28091     /**
28092      * @cfg {String} monthYearText
28093      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28094      */
28095     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28096     /**
28097      * @cfg {Number} startDay
28098      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28099      */
28100     startDay : 0,
28101     /**
28102      * @cfg {Bool} showClear
28103      * Show a clear button (usefull for date form elements that can be blank.)
28104      */
28105     
28106     showClear: false,
28107     
28108     /**
28109      * Sets the value of the date field
28110      * @param {Date} value The date to set
28111      */
28112     setValue : function(value){
28113         var old = this.value;
28114         
28115         if (typeof(value) == 'string') {
28116          
28117             value = Date.parseDate(value, this.format);
28118         }
28119         if (!value) {
28120             value = new Date();
28121         }
28122         
28123         this.value = value.clearTime(true);
28124         if(this.el){
28125             this.update(this.value);
28126         }
28127     },
28128
28129     /**
28130      * Gets the current selected value of the date field
28131      * @return {Date} The selected date
28132      */
28133     getValue : function(){
28134         return this.value;
28135     },
28136
28137     // private
28138     focus : function(){
28139         if(this.el){
28140             this.update(this.activeDate);
28141         }
28142     },
28143
28144     // privateval
28145     onRender : function(container, position){
28146         
28147         var m = [
28148              '<table cellspacing="0">',
28149                 '<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>',
28150                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28151         var dn = this.dayNames;
28152         for(var i = 0; i < 7; i++){
28153             var d = this.startDay+i;
28154             if(d > 6){
28155                 d = d-7;
28156             }
28157             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28158         }
28159         m[m.length] = "</tr></thead><tbody><tr>";
28160         for(var i = 0; i < 42; i++) {
28161             if(i % 7 == 0 && i != 0){
28162                 m[m.length] = "</tr><tr>";
28163             }
28164             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28165         }
28166         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28167             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28168
28169         var el = document.createElement("div");
28170         el.className = "x-date-picker";
28171         el.innerHTML = m.join("");
28172
28173         container.dom.insertBefore(el, position);
28174
28175         this.el = Roo.get(el);
28176         this.eventEl = Roo.get(el.firstChild);
28177
28178         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28179             handler: this.showPrevMonth,
28180             scope: this,
28181             preventDefault:true,
28182             stopDefault:true
28183         });
28184
28185         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28186             handler: this.showNextMonth,
28187             scope: this,
28188             preventDefault:true,
28189             stopDefault:true
28190         });
28191
28192         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28193
28194         this.monthPicker = this.el.down('div.x-date-mp');
28195         this.monthPicker.enableDisplayMode('block');
28196         
28197         var kn = new Roo.KeyNav(this.eventEl, {
28198             "left" : function(e){
28199                 e.ctrlKey ?
28200                     this.showPrevMonth() :
28201                     this.update(this.activeDate.add("d", -1));
28202             },
28203
28204             "right" : function(e){
28205                 e.ctrlKey ?
28206                     this.showNextMonth() :
28207                     this.update(this.activeDate.add("d", 1));
28208             },
28209
28210             "up" : function(e){
28211                 e.ctrlKey ?
28212                     this.showNextYear() :
28213                     this.update(this.activeDate.add("d", -7));
28214             },
28215
28216             "down" : function(e){
28217                 e.ctrlKey ?
28218                     this.showPrevYear() :
28219                     this.update(this.activeDate.add("d", 7));
28220             },
28221
28222             "pageUp" : function(e){
28223                 this.showNextMonth();
28224             },
28225
28226             "pageDown" : function(e){
28227                 this.showPrevMonth();
28228             },
28229
28230             "enter" : function(e){
28231                 e.stopPropagation();
28232                 return true;
28233             },
28234
28235             scope : this
28236         });
28237
28238         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28239
28240         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28241
28242         this.el.unselectable();
28243         
28244         this.cells = this.el.select("table.x-date-inner tbody td");
28245         this.textNodes = this.el.query("table.x-date-inner tbody span");
28246
28247         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28248             text: "&#160;",
28249             tooltip: this.monthYearText
28250         });
28251
28252         this.mbtn.on('click', this.showMonthPicker, this);
28253         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28254
28255
28256         var today = (new Date()).dateFormat(this.format);
28257         
28258         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28259         if (this.showClear) {
28260             baseTb.add( new Roo.Toolbar.Fill());
28261         }
28262         baseTb.add({
28263             text: String.format(this.todayText, today),
28264             tooltip: String.format(this.todayTip, today),
28265             handler: this.selectToday,
28266             scope: this
28267         });
28268         
28269         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28270             
28271         //});
28272         if (this.showClear) {
28273             
28274             baseTb.add( new Roo.Toolbar.Fill());
28275             baseTb.add({
28276                 text: '&#160;',
28277                 cls: 'x-btn-icon x-btn-clear',
28278                 handler: function() {
28279                     //this.value = '';
28280                     this.fireEvent("select", this, '');
28281                 },
28282                 scope: this
28283             });
28284         }
28285         
28286         
28287         if(Roo.isIE){
28288             this.el.repaint();
28289         }
28290         this.update(this.value);
28291     },
28292
28293     createMonthPicker : function(){
28294         if(!this.monthPicker.dom.firstChild){
28295             var buf = ['<table border="0" cellspacing="0">'];
28296             for(var i = 0; i < 6; i++){
28297                 buf.push(
28298                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28299                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28300                     i == 0 ?
28301                     '<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>' :
28302                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28303                 );
28304             }
28305             buf.push(
28306                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28307                     this.okText,
28308                     '</button><button type="button" class="x-date-mp-cancel">',
28309                     this.cancelText,
28310                     '</button></td></tr>',
28311                 '</table>'
28312             );
28313             this.monthPicker.update(buf.join(''));
28314             this.monthPicker.on('click', this.onMonthClick, this);
28315             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28316
28317             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28318             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28319
28320             this.mpMonths.each(function(m, a, i){
28321                 i += 1;
28322                 if((i%2) == 0){
28323                     m.dom.xmonth = 5 + Math.round(i * .5);
28324                 }else{
28325                     m.dom.xmonth = Math.round((i-1) * .5);
28326                 }
28327             });
28328         }
28329     },
28330
28331     showMonthPicker : function(){
28332         this.createMonthPicker();
28333         var size = this.el.getSize();
28334         this.monthPicker.setSize(size);
28335         this.monthPicker.child('table').setSize(size);
28336
28337         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28338         this.updateMPMonth(this.mpSelMonth);
28339         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28340         this.updateMPYear(this.mpSelYear);
28341
28342         this.monthPicker.slideIn('t', {duration:.2});
28343     },
28344
28345     updateMPYear : function(y){
28346         this.mpyear = y;
28347         var ys = this.mpYears.elements;
28348         for(var i = 1; i <= 10; i++){
28349             var td = ys[i-1], y2;
28350             if((i%2) == 0){
28351                 y2 = y + Math.round(i * .5);
28352                 td.firstChild.innerHTML = y2;
28353                 td.xyear = y2;
28354             }else{
28355                 y2 = y - (5-Math.round(i * .5));
28356                 td.firstChild.innerHTML = y2;
28357                 td.xyear = y2;
28358             }
28359             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28360         }
28361     },
28362
28363     updateMPMonth : function(sm){
28364         this.mpMonths.each(function(m, a, i){
28365             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28366         });
28367     },
28368
28369     selectMPMonth: function(m){
28370         
28371     },
28372
28373     onMonthClick : function(e, t){
28374         e.stopEvent();
28375         var el = new Roo.Element(t), pn;
28376         if(el.is('button.x-date-mp-cancel')){
28377             this.hideMonthPicker();
28378         }
28379         else if(el.is('button.x-date-mp-ok')){
28380             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28381             this.hideMonthPicker();
28382         }
28383         else if(pn = el.up('td.x-date-mp-month', 2)){
28384             this.mpMonths.removeClass('x-date-mp-sel');
28385             pn.addClass('x-date-mp-sel');
28386             this.mpSelMonth = pn.dom.xmonth;
28387         }
28388         else if(pn = el.up('td.x-date-mp-year', 2)){
28389             this.mpYears.removeClass('x-date-mp-sel');
28390             pn.addClass('x-date-mp-sel');
28391             this.mpSelYear = pn.dom.xyear;
28392         }
28393         else if(el.is('a.x-date-mp-prev')){
28394             this.updateMPYear(this.mpyear-10);
28395         }
28396         else if(el.is('a.x-date-mp-next')){
28397             this.updateMPYear(this.mpyear+10);
28398         }
28399     },
28400
28401     onMonthDblClick : function(e, t){
28402         e.stopEvent();
28403         var el = new Roo.Element(t), pn;
28404         if(pn = el.up('td.x-date-mp-month', 2)){
28405             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28406             this.hideMonthPicker();
28407         }
28408         else if(pn = el.up('td.x-date-mp-year', 2)){
28409             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28410             this.hideMonthPicker();
28411         }
28412     },
28413
28414     hideMonthPicker : function(disableAnim){
28415         if(this.monthPicker){
28416             if(disableAnim === true){
28417                 this.monthPicker.hide();
28418             }else{
28419                 this.monthPicker.slideOut('t', {duration:.2});
28420             }
28421         }
28422     },
28423
28424     // private
28425     showPrevMonth : function(e){
28426         this.update(this.activeDate.add("mo", -1));
28427     },
28428
28429     // private
28430     showNextMonth : function(e){
28431         this.update(this.activeDate.add("mo", 1));
28432     },
28433
28434     // private
28435     showPrevYear : function(){
28436         this.update(this.activeDate.add("y", -1));
28437     },
28438
28439     // private
28440     showNextYear : function(){
28441         this.update(this.activeDate.add("y", 1));
28442     },
28443
28444     // private
28445     handleMouseWheel : function(e){
28446         var delta = e.getWheelDelta();
28447         if(delta > 0){
28448             this.showPrevMonth();
28449             e.stopEvent();
28450         } else if(delta < 0){
28451             this.showNextMonth();
28452             e.stopEvent();
28453         }
28454     },
28455
28456     // private
28457     handleDateClick : function(e, t){
28458         e.stopEvent();
28459         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28460             this.setValue(new Date(t.dateValue));
28461             this.fireEvent("select", this, this.value);
28462         }
28463     },
28464
28465     // private
28466     selectToday : function(){
28467         this.setValue(new Date().clearTime());
28468         this.fireEvent("select", this, this.value);
28469     },
28470
28471     // private
28472     update : function(date)
28473     {
28474         var vd = this.activeDate;
28475         this.activeDate = date;
28476         if(vd && this.el){
28477             var t = date.getTime();
28478             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28479                 this.cells.removeClass("x-date-selected");
28480                 this.cells.each(function(c){
28481                    if(c.dom.firstChild.dateValue == t){
28482                        c.addClass("x-date-selected");
28483                        setTimeout(function(){
28484                             try{c.dom.firstChild.focus();}catch(e){}
28485                        }, 50);
28486                        return false;
28487                    }
28488                 });
28489                 return;
28490             }
28491         }
28492         
28493         var days = date.getDaysInMonth();
28494         var firstOfMonth = date.getFirstDateOfMonth();
28495         var startingPos = firstOfMonth.getDay()-this.startDay;
28496
28497         if(startingPos <= this.startDay){
28498             startingPos += 7;
28499         }
28500
28501         var pm = date.add("mo", -1);
28502         var prevStart = pm.getDaysInMonth()-startingPos;
28503
28504         var cells = this.cells.elements;
28505         var textEls = this.textNodes;
28506         days += startingPos;
28507
28508         // convert everything to numbers so it's fast
28509         var day = 86400000;
28510         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28511         var today = new Date().clearTime().getTime();
28512         var sel = date.clearTime().getTime();
28513         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28514         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28515         var ddMatch = this.disabledDatesRE;
28516         var ddText = this.disabledDatesText;
28517         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28518         var ddaysText = this.disabledDaysText;
28519         var format = this.format;
28520
28521         var setCellClass = function(cal, cell){
28522             cell.title = "";
28523             var t = d.getTime();
28524             cell.firstChild.dateValue = t;
28525             if(t == today){
28526                 cell.className += " x-date-today";
28527                 cell.title = cal.todayText;
28528             }
28529             if(t == sel){
28530                 cell.className += " x-date-selected";
28531                 setTimeout(function(){
28532                     try{cell.firstChild.focus();}catch(e){}
28533                 }, 50);
28534             }
28535             // disabling
28536             if(t < min) {
28537                 cell.className = " x-date-disabled";
28538                 cell.title = cal.minText;
28539                 return;
28540             }
28541             if(t > max) {
28542                 cell.className = " x-date-disabled";
28543                 cell.title = cal.maxText;
28544                 return;
28545             }
28546             if(ddays){
28547                 if(ddays.indexOf(d.getDay()) != -1){
28548                     cell.title = ddaysText;
28549                     cell.className = " x-date-disabled";
28550                 }
28551             }
28552             if(ddMatch && format){
28553                 var fvalue = d.dateFormat(format);
28554                 if(ddMatch.test(fvalue)){
28555                     cell.title = ddText.replace("%0", fvalue);
28556                     cell.className = " x-date-disabled";
28557                 }
28558             }
28559         };
28560
28561         var i = 0;
28562         for(; i < startingPos; i++) {
28563             textEls[i].innerHTML = (++prevStart);
28564             d.setDate(d.getDate()+1);
28565             cells[i].className = "x-date-prevday";
28566             setCellClass(this, cells[i]);
28567         }
28568         for(; i < days; i++){
28569             intDay = i - startingPos + 1;
28570             textEls[i].innerHTML = (intDay);
28571             d.setDate(d.getDate()+1);
28572             cells[i].className = "x-date-active";
28573             setCellClass(this, cells[i]);
28574         }
28575         var extraDays = 0;
28576         for(; i < 42; i++) {
28577              textEls[i].innerHTML = (++extraDays);
28578              d.setDate(d.getDate()+1);
28579              cells[i].className = "x-date-nextday";
28580              setCellClass(this, cells[i]);
28581         }
28582
28583         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28584         this.fireEvent('monthchange', this, date);
28585         
28586         if(!this.internalRender){
28587             var main = this.el.dom.firstChild;
28588             var w = main.offsetWidth;
28589             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28590             Roo.fly(main).setWidth(w);
28591             this.internalRender = true;
28592             // opera does not respect the auto grow header center column
28593             // then, after it gets a width opera refuses to recalculate
28594             // without a second pass
28595             if(Roo.isOpera && !this.secondPass){
28596                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28597                 this.secondPass = true;
28598                 this.update.defer(10, this, [date]);
28599             }
28600         }
28601         
28602         
28603     }
28604 });        /*
28605  * Based on:
28606  * Ext JS Library 1.1.1
28607  * Copyright(c) 2006-2007, Ext JS, LLC.
28608  *
28609  * Originally Released Under LGPL - original licence link has changed is not relivant.
28610  *
28611  * Fork - LGPL
28612  * <script type="text/javascript">
28613  */
28614 /**
28615  * @class Roo.TabPanel
28616  * @extends Roo.util.Observable
28617  * A lightweight tab container.
28618  * <br><br>
28619  * Usage:
28620  * <pre><code>
28621 // basic tabs 1, built from existing content
28622 var tabs = new Roo.TabPanel("tabs1");
28623 tabs.addTab("script", "View Script");
28624 tabs.addTab("markup", "View Markup");
28625 tabs.activate("script");
28626
28627 // more advanced tabs, built from javascript
28628 var jtabs = new Roo.TabPanel("jtabs");
28629 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28630
28631 // set up the UpdateManager
28632 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28633 var updater = tab2.getUpdateManager();
28634 updater.setDefaultUrl("ajax1.htm");
28635 tab2.on('activate', updater.refresh, updater, true);
28636
28637 // Use setUrl for Ajax loading
28638 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28639 tab3.setUrl("ajax2.htm", null, true);
28640
28641 // Disabled tab
28642 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28643 tab4.disable();
28644
28645 jtabs.activate("jtabs-1");
28646  * </code></pre>
28647  * @constructor
28648  * Create a new TabPanel.
28649  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28650  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28651  */
28652 Roo.TabPanel = function(container, config){
28653     /**
28654     * The container element for this TabPanel.
28655     * @type Roo.Element
28656     */
28657     this.el = Roo.get(container, true);
28658     if(config){
28659         if(typeof config == "boolean"){
28660             this.tabPosition = config ? "bottom" : "top";
28661         }else{
28662             Roo.apply(this, config);
28663         }
28664     }
28665     if(this.tabPosition == "bottom"){
28666         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28667         this.el.addClass("x-tabs-bottom");
28668     }
28669     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28670     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28671     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28672     if(Roo.isIE){
28673         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28674     }
28675     if(this.tabPosition != "bottom"){
28676         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28677          * @type Roo.Element
28678          */
28679         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28680         this.el.addClass("x-tabs-top");
28681     }
28682     this.items = [];
28683
28684     this.bodyEl.setStyle("position", "relative");
28685
28686     this.active = null;
28687     this.activateDelegate = this.activate.createDelegate(this);
28688
28689     this.addEvents({
28690         /**
28691          * @event tabchange
28692          * Fires when the active tab changes
28693          * @param {Roo.TabPanel} this
28694          * @param {Roo.TabPanelItem} activePanel The new active tab
28695          */
28696         "tabchange": true,
28697         /**
28698          * @event beforetabchange
28699          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28700          * @param {Roo.TabPanel} this
28701          * @param {Object} e Set cancel to true on this object to cancel the tab change
28702          * @param {Roo.TabPanelItem} tab The tab being changed to
28703          */
28704         "beforetabchange" : true
28705     });
28706
28707     Roo.EventManager.onWindowResize(this.onResize, this);
28708     this.cpad = this.el.getPadding("lr");
28709     this.hiddenCount = 0;
28710
28711
28712     // toolbar on the tabbar support...
28713     if (this.toolbar) {
28714         var tcfg = this.toolbar;
28715         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28716         this.toolbar = new Roo.Toolbar(tcfg);
28717         if (Roo.isSafari) {
28718             var tbl = tcfg.container.child('table', true);
28719             tbl.setAttribute('width', '100%');
28720         }
28721         
28722     }
28723    
28724
28725
28726     Roo.TabPanel.superclass.constructor.call(this);
28727 };
28728
28729 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28730     /*
28731      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28732      */
28733     tabPosition : "top",
28734     /*
28735      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28736      */
28737     currentTabWidth : 0,
28738     /*
28739      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28740      */
28741     minTabWidth : 40,
28742     /*
28743      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28744      */
28745     maxTabWidth : 250,
28746     /*
28747      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28748      */
28749     preferredTabWidth : 175,
28750     /*
28751      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28752      */
28753     resizeTabs : false,
28754     /*
28755      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28756      */
28757     monitorResize : true,
28758     /*
28759      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28760      */
28761     toolbar : false,
28762
28763     /**
28764      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28765      * @param {String} id The id of the div to use <b>or create</b>
28766      * @param {String} text The text for the tab
28767      * @param {String} content (optional) Content to put in the TabPanelItem body
28768      * @param {Boolean} closable (optional) True to create a close icon on the tab
28769      * @return {Roo.TabPanelItem} The created TabPanelItem
28770      */
28771     addTab : function(id, text, content, closable){
28772         var item = new Roo.TabPanelItem(this, id, text, closable);
28773         this.addTabItem(item);
28774         if(content){
28775             item.setContent(content);
28776         }
28777         return item;
28778     },
28779
28780     /**
28781      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28782      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28783      * @return {Roo.TabPanelItem}
28784      */
28785     getTab : function(id){
28786         return this.items[id];
28787     },
28788
28789     /**
28790      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28791      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28792      */
28793     hideTab : function(id){
28794         var t = this.items[id];
28795         if(!t.isHidden()){
28796            t.setHidden(true);
28797            this.hiddenCount++;
28798            this.autoSizeTabs();
28799         }
28800     },
28801
28802     /**
28803      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28804      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28805      */
28806     unhideTab : function(id){
28807         var t = this.items[id];
28808         if(t.isHidden()){
28809            t.setHidden(false);
28810            this.hiddenCount--;
28811            this.autoSizeTabs();
28812         }
28813     },
28814
28815     /**
28816      * Adds an existing {@link Roo.TabPanelItem}.
28817      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28818      */
28819     addTabItem : function(item){
28820         this.items[item.id] = item;
28821         this.items.push(item);
28822         if(this.resizeTabs){
28823            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28824            this.autoSizeTabs();
28825         }else{
28826             item.autoSize();
28827         }
28828     },
28829
28830     /**
28831      * Removes a {@link Roo.TabPanelItem}.
28832      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28833      */
28834     removeTab : function(id){
28835         var items = this.items;
28836         var tab = items[id];
28837         if(!tab) { return; }
28838         var index = items.indexOf(tab);
28839         if(this.active == tab && items.length > 1){
28840             var newTab = this.getNextAvailable(index);
28841             if(newTab) {
28842                 newTab.activate();
28843             }
28844         }
28845         this.stripEl.dom.removeChild(tab.pnode.dom);
28846         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28847             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28848         }
28849         items.splice(index, 1);
28850         delete this.items[tab.id];
28851         tab.fireEvent("close", tab);
28852         tab.purgeListeners();
28853         this.autoSizeTabs();
28854     },
28855
28856     getNextAvailable : function(start){
28857         var items = this.items;
28858         var index = start;
28859         // look for a next tab that will slide over to
28860         // replace the one being removed
28861         while(index < items.length){
28862             var item = items[++index];
28863             if(item && !item.isHidden()){
28864                 return item;
28865             }
28866         }
28867         // if one isn't found select the previous tab (on the left)
28868         index = start;
28869         while(index >= 0){
28870             var item = items[--index];
28871             if(item && !item.isHidden()){
28872                 return item;
28873             }
28874         }
28875         return null;
28876     },
28877
28878     /**
28879      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28880      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28881      */
28882     disableTab : function(id){
28883         var tab = this.items[id];
28884         if(tab && this.active != tab){
28885             tab.disable();
28886         }
28887     },
28888
28889     /**
28890      * Enables a {@link Roo.TabPanelItem} that is disabled.
28891      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28892      */
28893     enableTab : function(id){
28894         var tab = this.items[id];
28895         tab.enable();
28896     },
28897
28898     /**
28899      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28900      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28901      * @return {Roo.TabPanelItem} The TabPanelItem.
28902      */
28903     activate : function(id){
28904         var tab = this.items[id];
28905         if(!tab){
28906             return null;
28907         }
28908         if(tab == this.active || tab.disabled){
28909             return tab;
28910         }
28911         var e = {};
28912         this.fireEvent("beforetabchange", this, e, tab);
28913         if(e.cancel !== true && !tab.disabled){
28914             if(this.active){
28915                 this.active.hide();
28916             }
28917             this.active = this.items[id];
28918             this.active.show();
28919             this.fireEvent("tabchange", this, this.active);
28920         }
28921         return tab;
28922     },
28923
28924     /**
28925      * Gets the active {@link Roo.TabPanelItem}.
28926      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28927      */
28928     getActiveTab : function(){
28929         return this.active;
28930     },
28931
28932     /**
28933      * Updates the tab body element to fit the height of the container element
28934      * for overflow scrolling
28935      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28936      */
28937     syncHeight : function(targetHeight){
28938         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28939         var bm = this.bodyEl.getMargins();
28940         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28941         this.bodyEl.setHeight(newHeight);
28942         return newHeight;
28943     },
28944
28945     onResize : function(){
28946         if(this.monitorResize){
28947             this.autoSizeTabs();
28948         }
28949     },
28950
28951     /**
28952      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28953      */
28954     beginUpdate : function(){
28955         this.updating = true;
28956     },
28957
28958     /**
28959      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28960      */
28961     endUpdate : function(){
28962         this.updating = false;
28963         this.autoSizeTabs();
28964     },
28965
28966     /**
28967      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28968      */
28969     autoSizeTabs : function(){
28970         var count = this.items.length;
28971         var vcount = count - this.hiddenCount;
28972         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28973             return;
28974         }
28975         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28976         var availWidth = Math.floor(w / vcount);
28977         var b = this.stripBody;
28978         if(b.getWidth() > w){
28979             var tabs = this.items;
28980             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28981             if(availWidth < this.minTabWidth){
28982                 /*if(!this.sleft){    // incomplete scrolling code
28983                     this.createScrollButtons();
28984                 }
28985                 this.showScroll();
28986                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28987             }
28988         }else{
28989             if(this.currentTabWidth < this.preferredTabWidth){
28990                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28991             }
28992         }
28993     },
28994
28995     /**
28996      * Returns the number of tabs in this TabPanel.
28997      * @return {Number}
28998      */
28999      getCount : function(){
29000          return this.items.length;
29001      },
29002
29003     /**
29004      * Resizes all the tabs to the passed width
29005      * @param {Number} The new width
29006      */
29007     setTabWidth : function(width){
29008         this.currentTabWidth = width;
29009         for(var i = 0, len = this.items.length; i < len; i++) {
29010                 if(!this.items[i].isHidden()) {
29011                 this.items[i].setWidth(width);
29012             }
29013         }
29014     },
29015
29016     /**
29017      * Destroys this TabPanel
29018      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29019      */
29020     destroy : function(removeEl){
29021         Roo.EventManager.removeResizeListener(this.onResize, this);
29022         for(var i = 0, len = this.items.length; i < len; i++){
29023             this.items[i].purgeListeners();
29024         }
29025         if(removeEl === true){
29026             this.el.update("");
29027             this.el.remove();
29028         }
29029     }
29030 });
29031
29032 /**
29033  * @class Roo.TabPanelItem
29034  * @extends Roo.util.Observable
29035  * Represents an individual item (tab plus body) in a TabPanel.
29036  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29037  * @param {String} id The id of this TabPanelItem
29038  * @param {String} text The text for the tab of this TabPanelItem
29039  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29040  */
29041 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29042     /**
29043      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29044      * @type Roo.TabPanel
29045      */
29046     this.tabPanel = tabPanel;
29047     /**
29048      * The id for this TabPanelItem
29049      * @type String
29050      */
29051     this.id = id;
29052     /** @private */
29053     this.disabled = false;
29054     /** @private */
29055     this.text = text;
29056     /** @private */
29057     this.loaded = false;
29058     this.closable = closable;
29059
29060     /**
29061      * The body element for this TabPanelItem.
29062      * @type Roo.Element
29063      */
29064     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29065     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29066     this.bodyEl.setStyle("display", "block");
29067     this.bodyEl.setStyle("zoom", "1");
29068     this.hideAction();
29069
29070     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29071     /** @private */
29072     this.el = Roo.get(els.el, true);
29073     this.inner = Roo.get(els.inner, true);
29074     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29075     this.pnode = Roo.get(els.el.parentNode, true);
29076     this.el.on("mousedown", this.onTabMouseDown, this);
29077     this.el.on("click", this.onTabClick, this);
29078     /** @private */
29079     if(closable){
29080         var c = Roo.get(els.close, true);
29081         c.dom.title = this.closeText;
29082         c.addClassOnOver("close-over");
29083         c.on("click", this.closeClick, this);
29084      }
29085
29086     this.addEvents({
29087          /**
29088          * @event activate
29089          * Fires when this tab becomes the active tab.
29090          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29091          * @param {Roo.TabPanelItem} this
29092          */
29093         "activate": true,
29094         /**
29095          * @event beforeclose
29096          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29097          * @param {Roo.TabPanelItem} this
29098          * @param {Object} e Set cancel to true on this object to cancel the close.
29099          */
29100         "beforeclose": true,
29101         /**
29102          * @event close
29103          * Fires when this tab is closed.
29104          * @param {Roo.TabPanelItem} this
29105          */
29106          "close": true,
29107         /**
29108          * @event deactivate
29109          * Fires when this tab is no longer the active tab.
29110          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29111          * @param {Roo.TabPanelItem} this
29112          */
29113          "deactivate" : true
29114     });
29115     this.hidden = false;
29116
29117     Roo.TabPanelItem.superclass.constructor.call(this);
29118 };
29119
29120 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29121     purgeListeners : function(){
29122        Roo.util.Observable.prototype.purgeListeners.call(this);
29123        this.el.removeAllListeners();
29124     },
29125     /**
29126      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29127      */
29128     show : function(){
29129         this.pnode.addClass("on");
29130         this.showAction();
29131         if(Roo.isOpera){
29132             this.tabPanel.stripWrap.repaint();
29133         }
29134         this.fireEvent("activate", this.tabPanel, this);
29135     },
29136
29137     /**
29138      * Returns true if this tab is the active tab.
29139      * @return {Boolean}
29140      */
29141     isActive : function(){
29142         return this.tabPanel.getActiveTab() == this;
29143     },
29144
29145     /**
29146      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29147      */
29148     hide : function(){
29149         this.pnode.removeClass("on");
29150         this.hideAction();
29151         this.fireEvent("deactivate", this.tabPanel, this);
29152     },
29153
29154     hideAction : function(){
29155         this.bodyEl.hide();
29156         this.bodyEl.setStyle("position", "absolute");
29157         this.bodyEl.setLeft("-20000px");
29158         this.bodyEl.setTop("-20000px");
29159     },
29160
29161     showAction : function(){
29162         this.bodyEl.setStyle("position", "relative");
29163         this.bodyEl.setTop("");
29164         this.bodyEl.setLeft("");
29165         this.bodyEl.show();
29166     },
29167
29168     /**
29169      * Set the tooltip for the tab.
29170      * @param {String} tooltip The tab's tooltip
29171      */
29172     setTooltip : function(text){
29173         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29174             this.textEl.dom.qtip = text;
29175             this.textEl.dom.removeAttribute('title');
29176         }else{
29177             this.textEl.dom.title = text;
29178         }
29179     },
29180
29181     onTabClick : function(e){
29182         e.preventDefault();
29183         this.tabPanel.activate(this.id);
29184     },
29185
29186     onTabMouseDown : function(e){
29187         e.preventDefault();
29188         this.tabPanel.activate(this.id);
29189     },
29190
29191     getWidth : function(){
29192         return this.inner.getWidth();
29193     },
29194
29195     setWidth : function(width){
29196         var iwidth = width - this.pnode.getPadding("lr");
29197         this.inner.setWidth(iwidth);
29198         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29199         this.pnode.setWidth(width);
29200     },
29201
29202     /**
29203      * Show or hide the tab
29204      * @param {Boolean} hidden True to hide or false to show.
29205      */
29206     setHidden : function(hidden){
29207         this.hidden = hidden;
29208         this.pnode.setStyle("display", hidden ? "none" : "");
29209     },
29210
29211     /**
29212      * Returns true if this tab is "hidden"
29213      * @return {Boolean}
29214      */
29215     isHidden : function(){
29216         return this.hidden;
29217     },
29218
29219     /**
29220      * Returns the text for this tab
29221      * @return {String}
29222      */
29223     getText : function(){
29224         return this.text;
29225     },
29226
29227     autoSize : function(){
29228         //this.el.beginMeasure();
29229         this.textEl.setWidth(1);
29230         /*
29231          *  #2804 [new] Tabs in Roojs
29232          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29233          */
29234         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29235         //this.el.endMeasure();
29236     },
29237
29238     /**
29239      * Sets the text for the tab (Note: this also sets the tooltip text)
29240      * @param {String} text The tab's text and tooltip
29241      */
29242     setText : function(text){
29243         this.text = text;
29244         this.textEl.update(text);
29245         this.setTooltip(text);
29246         if(!this.tabPanel.resizeTabs){
29247             this.autoSize();
29248         }
29249     },
29250     /**
29251      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29252      */
29253     activate : function(){
29254         this.tabPanel.activate(this.id);
29255     },
29256
29257     /**
29258      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29259      */
29260     disable : function(){
29261         if(this.tabPanel.active != this){
29262             this.disabled = true;
29263             this.pnode.addClass("disabled");
29264         }
29265     },
29266
29267     /**
29268      * Enables this TabPanelItem if it was previously disabled.
29269      */
29270     enable : function(){
29271         this.disabled = false;
29272         this.pnode.removeClass("disabled");
29273     },
29274
29275     /**
29276      * Sets the content for this TabPanelItem.
29277      * @param {String} content The content
29278      * @param {Boolean} loadScripts true to look for and load scripts
29279      */
29280     setContent : function(content, loadScripts){
29281         this.bodyEl.update(content, loadScripts);
29282     },
29283
29284     /**
29285      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29286      * @return {Roo.UpdateManager} The UpdateManager
29287      */
29288     getUpdateManager : function(){
29289         return this.bodyEl.getUpdateManager();
29290     },
29291
29292     /**
29293      * Set a URL to be used to load the content for this TabPanelItem.
29294      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29295      * @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)
29296      * @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)
29297      * @return {Roo.UpdateManager} The UpdateManager
29298      */
29299     setUrl : function(url, params, loadOnce){
29300         if(this.refreshDelegate){
29301             this.un('activate', this.refreshDelegate);
29302         }
29303         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29304         this.on("activate", this.refreshDelegate);
29305         return this.bodyEl.getUpdateManager();
29306     },
29307
29308     /** @private */
29309     _handleRefresh : function(url, params, loadOnce){
29310         if(!loadOnce || !this.loaded){
29311             var updater = this.bodyEl.getUpdateManager();
29312             updater.update(url, params, this._setLoaded.createDelegate(this));
29313         }
29314     },
29315
29316     /**
29317      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29318      *   Will fail silently if the setUrl method has not been called.
29319      *   This does not activate the panel, just updates its content.
29320      */
29321     refresh : function(){
29322         if(this.refreshDelegate){
29323            this.loaded = false;
29324            this.refreshDelegate();
29325         }
29326     },
29327
29328     /** @private */
29329     _setLoaded : function(){
29330         this.loaded = true;
29331     },
29332
29333     /** @private */
29334     closeClick : function(e){
29335         var o = {};
29336         e.stopEvent();
29337         this.fireEvent("beforeclose", this, o);
29338         if(o.cancel !== true){
29339             this.tabPanel.removeTab(this.id);
29340         }
29341     },
29342     /**
29343      * The text displayed in the tooltip for the close icon.
29344      * @type String
29345      */
29346     closeText : "Close this tab"
29347 });
29348
29349 /** @private */
29350 Roo.TabPanel.prototype.createStrip = function(container){
29351     var strip = document.createElement("div");
29352     strip.className = "x-tabs-wrap";
29353     container.appendChild(strip);
29354     return strip;
29355 };
29356 /** @private */
29357 Roo.TabPanel.prototype.createStripList = function(strip){
29358     // div wrapper for retard IE
29359     // returns the "tr" element.
29360     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29361         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29362         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29363     return strip.firstChild.firstChild.firstChild.firstChild;
29364 };
29365 /** @private */
29366 Roo.TabPanel.prototype.createBody = function(container){
29367     var body = document.createElement("div");
29368     Roo.id(body, "tab-body");
29369     Roo.fly(body).addClass("x-tabs-body");
29370     container.appendChild(body);
29371     return body;
29372 };
29373 /** @private */
29374 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29375     var body = Roo.getDom(id);
29376     if(!body){
29377         body = document.createElement("div");
29378         body.id = id;
29379     }
29380     Roo.fly(body).addClass("x-tabs-item-body");
29381     bodyEl.insertBefore(body, bodyEl.firstChild);
29382     return body;
29383 };
29384 /** @private */
29385 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29386     var td = document.createElement("td");
29387     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29388     //stripEl.appendChild(td);
29389     if(closable){
29390         td.className = "x-tabs-closable";
29391         if(!this.closeTpl){
29392             this.closeTpl = new Roo.Template(
29393                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29394                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29395                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29396             );
29397         }
29398         var el = this.closeTpl.overwrite(td, {"text": text});
29399         var close = el.getElementsByTagName("div")[0];
29400         var inner = el.getElementsByTagName("em")[0];
29401         return {"el": el, "close": close, "inner": inner};
29402     } else {
29403         if(!this.tabTpl){
29404             this.tabTpl = new Roo.Template(
29405                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29406                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29407             );
29408         }
29409         var el = this.tabTpl.overwrite(td, {"text": text});
29410         var inner = el.getElementsByTagName("em")[0];
29411         return {"el": el, "inner": inner};
29412     }
29413 };/*
29414  * Based on:
29415  * Ext JS Library 1.1.1
29416  * Copyright(c) 2006-2007, Ext JS, LLC.
29417  *
29418  * Originally Released Under LGPL - original licence link has changed is not relivant.
29419  *
29420  * Fork - LGPL
29421  * <script type="text/javascript">
29422  */
29423
29424 /**
29425  * @class Roo.Button
29426  * @extends Roo.util.Observable
29427  * Simple Button class
29428  * @cfg {String} text The button text
29429  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29430  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29431  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29432  * @cfg {Object} scope The scope of the handler
29433  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29434  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29435  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29436  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29437  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29438  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29439    applies if enableToggle = true)
29440  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29441  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29442   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29443  * @constructor
29444  * Create a new button
29445  * @param {Object} config The config object
29446  */
29447 Roo.Button = function(renderTo, config)
29448 {
29449     if (!config) {
29450         config = renderTo;
29451         renderTo = config.renderTo || false;
29452     }
29453     
29454     Roo.apply(this, config);
29455     this.addEvents({
29456         /**
29457              * @event click
29458              * Fires when this button is clicked
29459              * @param {Button} this
29460              * @param {EventObject} e The click event
29461              */
29462             "click" : true,
29463         /**
29464              * @event toggle
29465              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29466              * @param {Button} this
29467              * @param {Boolean} pressed
29468              */
29469             "toggle" : true,
29470         /**
29471              * @event mouseover
29472              * Fires when the mouse hovers over the button
29473              * @param {Button} this
29474              * @param {Event} e The event object
29475              */
29476         'mouseover' : true,
29477         /**
29478              * @event mouseout
29479              * Fires when the mouse exits the button
29480              * @param {Button} this
29481              * @param {Event} e The event object
29482              */
29483         'mouseout': true,
29484          /**
29485              * @event render
29486              * Fires when the button is rendered
29487              * @param {Button} this
29488              */
29489         'render': true
29490     });
29491     if(this.menu){
29492         this.menu = Roo.menu.MenuMgr.get(this.menu);
29493     }
29494     // register listeners first!!  - so render can be captured..
29495     Roo.util.Observable.call(this);
29496     if(renderTo){
29497         this.render(renderTo);
29498     }
29499     
29500   
29501 };
29502
29503 Roo.extend(Roo.Button, Roo.util.Observable, {
29504     /**
29505      * 
29506      */
29507     
29508     /**
29509      * Read-only. True if this button is hidden
29510      * @type Boolean
29511      */
29512     hidden : false,
29513     /**
29514      * Read-only. True if this button is disabled
29515      * @type Boolean
29516      */
29517     disabled : false,
29518     /**
29519      * Read-only. True if this button is pressed (only if enableToggle = true)
29520      * @type Boolean
29521      */
29522     pressed : false,
29523
29524     /**
29525      * @cfg {Number} tabIndex 
29526      * The DOM tabIndex for this button (defaults to undefined)
29527      */
29528     tabIndex : undefined,
29529
29530     /**
29531      * @cfg {Boolean} enableToggle
29532      * True to enable pressed/not pressed toggling (defaults to false)
29533      */
29534     enableToggle: false,
29535     /**
29536      * @cfg {Roo.menu.Menu} menu
29537      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29538      */
29539     menu : undefined,
29540     /**
29541      * @cfg {String} menuAlign
29542      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29543      */
29544     menuAlign : "tl-bl?",
29545
29546     /**
29547      * @cfg {String} iconCls
29548      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29549      */
29550     iconCls : undefined,
29551     /**
29552      * @cfg {String} type
29553      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29554      */
29555     type : 'button',
29556
29557     // private
29558     menuClassTarget: 'tr',
29559
29560     /**
29561      * @cfg {String} clickEvent
29562      * The type of event to map to the button's event handler (defaults to 'click')
29563      */
29564     clickEvent : 'click',
29565
29566     /**
29567      * @cfg {Boolean} handleMouseEvents
29568      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29569      */
29570     handleMouseEvents : true,
29571
29572     /**
29573      * @cfg {String} tooltipType
29574      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29575      */
29576     tooltipType : 'qtip',
29577
29578     /**
29579      * @cfg {String} cls
29580      * A CSS class to apply to the button's main element.
29581      */
29582     
29583     /**
29584      * @cfg {Roo.Template} template (Optional)
29585      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29586      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29587      * require code modifications if required elements (e.g. a button) aren't present.
29588      */
29589
29590     // private
29591     render : function(renderTo){
29592         var btn;
29593         if(this.hideParent){
29594             this.parentEl = Roo.get(renderTo);
29595         }
29596         if(!this.dhconfig){
29597             if(!this.template){
29598                 if(!Roo.Button.buttonTemplate){
29599                     // hideous table template
29600                     Roo.Button.buttonTemplate = new Roo.Template(
29601                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29602                         '<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>',
29603                         "</tr></tbody></table>");
29604                 }
29605                 this.template = Roo.Button.buttonTemplate;
29606             }
29607             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29608             var btnEl = btn.child("button:first");
29609             btnEl.on('focus', this.onFocus, this);
29610             btnEl.on('blur', this.onBlur, this);
29611             if(this.cls){
29612                 btn.addClass(this.cls);
29613             }
29614             if(this.icon){
29615                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29616             }
29617             if(this.iconCls){
29618                 btnEl.addClass(this.iconCls);
29619                 if(!this.cls){
29620                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29621                 }
29622             }
29623             if(this.tabIndex !== undefined){
29624                 btnEl.dom.tabIndex = this.tabIndex;
29625             }
29626             if(this.tooltip){
29627                 if(typeof this.tooltip == 'object'){
29628                     Roo.QuickTips.tips(Roo.apply({
29629                           target: btnEl.id
29630                     }, this.tooltip));
29631                 } else {
29632                     btnEl.dom[this.tooltipType] = this.tooltip;
29633                 }
29634             }
29635         }else{
29636             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29637         }
29638         this.el = btn;
29639         if(this.id){
29640             this.el.dom.id = this.el.id = this.id;
29641         }
29642         if(this.menu){
29643             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29644             this.menu.on("show", this.onMenuShow, this);
29645             this.menu.on("hide", this.onMenuHide, this);
29646         }
29647         btn.addClass("x-btn");
29648         if(Roo.isIE && !Roo.isIE7){
29649             this.autoWidth.defer(1, this);
29650         }else{
29651             this.autoWidth();
29652         }
29653         if(this.handleMouseEvents){
29654             btn.on("mouseover", this.onMouseOver, this);
29655             btn.on("mouseout", this.onMouseOut, this);
29656             btn.on("mousedown", this.onMouseDown, this);
29657         }
29658         btn.on(this.clickEvent, this.onClick, this);
29659         //btn.on("mouseup", this.onMouseUp, this);
29660         if(this.hidden){
29661             this.hide();
29662         }
29663         if(this.disabled){
29664             this.disable();
29665         }
29666         Roo.ButtonToggleMgr.register(this);
29667         if(this.pressed){
29668             this.el.addClass("x-btn-pressed");
29669         }
29670         if(this.repeat){
29671             var repeater = new Roo.util.ClickRepeater(btn,
29672                 typeof this.repeat == "object" ? this.repeat : {}
29673             );
29674             repeater.on("click", this.onClick,  this);
29675         }
29676         
29677         this.fireEvent('render', this);
29678         
29679     },
29680     /**
29681      * Returns the button's underlying element
29682      * @return {Roo.Element} The element
29683      */
29684     getEl : function(){
29685         return this.el;  
29686     },
29687     
29688     /**
29689      * Destroys this Button and removes any listeners.
29690      */
29691     destroy : function(){
29692         Roo.ButtonToggleMgr.unregister(this);
29693         this.el.removeAllListeners();
29694         this.purgeListeners();
29695         this.el.remove();
29696     },
29697
29698     // private
29699     autoWidth : function(){
29700         if(this.el){
29701             this.el.setWidth("auto");
29702             if(Roo.isIE7 && Roo.isStrict){
29703                 var ib = this.el.child('button');
29704                 if(ib && ib.getWidth() > 20){
29705                     ib.clip();
29706                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29707                 }
29708             }
29709             if(this.minWidth){
29710                 if(this.hidden){
29711                     this.el.beginMeasure();
29712                 }
29713                 if(this.el.getWidth() < this.minWidth){
29714                     this.el.setWidth(this.minWidth);
29715                 }
29716                 if(this.hidden){
29717                     this.el.endMeasure();
29718                 }
29719             }
29720         }
29721     },
29722
29723     /**
29724      * Assigns this button's click handler
29725      * @param {Function} handler The function to call when the button is clicked
29726      * @param {Object} scope (optional) Scope for the function passed in
29727      */
29728     setHandler : function(handler, scope){
29729         this.handler = handler;
29730         this.scope = scope;  
29731     },
29732     
29733     /**
29734      * Sets this button's text
29735      * @param {String} text The button text
29736      */
29737     setText : function(text){
29738         this.text = text;
29739         if(this.el){
29740             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29741         }
29742         this.autoWidth();
29743     },
29744     
29745     /**
29746      * Gets the text for this button
29747      * @return {String} The button text
29748      */
29749     getText : function(){
29750         return this.text;  
29751     },
29752     
29753     /**
29754      * Show this button
29755      */
29756     show: function(){
29757         this.hidden = false;
29758         if(this.el){
29759             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29760         }
29761     },
29762     
29763     /**
29764      * Hide this button
29765      */
29766     hide: function(){
29767         this.hidden = true;
29768         if(this.el){
29769             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29770         }
29771     },
29772     
29773     /**
29774      * Convenience function for boolean show/hide
29775      * @param {Boolean} visible True to show, false to hide
29776      */
29777     setVisible: function(visible){
29778         if(visible) {
29779             this.show();
29780         }else{
29781             this.hide();
29782         }
29783     },
29784     
29785     /**
29786      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29787      * @param {Boolean} state (optional) Force a particular state
29788      */
29789     toggle : function(state){
29790         state = state === undefined ? !this.pressed : state;
29791         if(state != this.pressed){
29792             if(state){
29793                 this.el.addClass("x-btn-pressed");
29794                 this.pressed = true;
29795                 this.fireEvent("toggle", this, true);
29796             }else{
29797                 this.el.removeClass("x-btn-pressed");
29798                 this.pressed = false;
29799                 this.fireEvent("toggle", this, false);
29800             }
29801             if(this.toggleHandler){
29802                 this.toggleHandler.call(this.scope || this, this, state);
29803             }
29804         }
29805     },
29806     
29807     /**
29808      * Focus the button
29809      */
29810     focus : function(){
29811         this.el.child('button:first').focus();
29812     },
29813     
29814     /**
29815      * Disable this button
29816      */
29817     disable : function(){
29818         if(this.el){
29819             this.el.addClass("x-btn-disabled");
29820         }
29821         this.disabled = true;
29822     },
29823     
29824     /**
29825      * Enable this button
29826      */
29827     enable : function(){
29828         if(this.el){
29829             this.el.removeClass("x-btn-disabled");
29830         }
29831         this.disabled = false;
29832     },
29833
29834     /**
29835      * Convenience function for boolean enable/disable
29836      * @param {Boolean} enabled True to enable, false to disable
29837      */
29838     setDisabled : function(v){
29839         this[v !== true ? "enable" : "disable"]();
29840     },
29841
29842     // private
29843     onClick : function(e)
29844     {
29845         if(e){
29846             e.preventDefault();
29847         }
29848         if(e.button != 0){
29849             return;
29850         }
29851         if(!this.disabled){
29852             if(this.enableToggle){
29853                 this.toggle();
29854             }
29855             if(this.menu && !this.menu.isVisible()){
29856                 this.menu.show(this.el, this.menuAlign);
29857             }
29858             this.fireEvent("click", this, e);
29859             if(this.handler){
29860                 this.el.removeClass("x-btn-over");
29861                 this.handler.call(this.scope || this, this, e);
29862             }
29863         }
29864     },
29865     // private
29866     onMouseOver : function(e){
29867         if(!this.disabled){
29868             this.el.addClass("x-btn-over");
29869             this.fireEvent('mouseover', this, e);
29870         }
29871     },
29872     // private
29873     onMouseOut : function(e){
29874         if(!e.within(this.el,  true)){
29875             this.el.removeClass("x-btn-over");
29876             this.fireEvent('mouseout', this, e);
29877         }
29878     },
29879     // private
29880     onFocus : function(e){
29881         if(!this.disabled){
29882             this.el.addClass("x-btn-focus");
29883         }
29884     },
29885     // private
29886     onBlur : function(e){
29887         this.el.removeClass("x-btn-focus");
29888     },
29889     // private
29890     onMouseDown : function(e){
29891         if(!this.disabled && e.button == 0){
29892             this.el.addClass("x-btn-click");
29893             Roo.get(document).on('mouseup', this.onMouseUp, this);
29894         }
29895     },
29896     // private
29897     onMouseUp : function(e){
29898         if(e.button == 0){
29899             this.el.removeClass("x-btn-click");
29900             Roo.get(document).un('mouseup', this.onMouseUp, this);
29901         }
29902     },
29903     // private
29904     onMenuShow : function(e){
29905         this.el.addClass("x-btn-menu-active");
29906     },
29907     // private
29908     onMenuHide : function(e){
29909         this.el.removeClass("x-btn-menu-active");
29910     }   
29911 });
29912
29913 // Private utility class used by Button
29914 Roo.ButtonToggleMgr = function(){
29915    var groups = {};
29916    
29917    function toggleGroup(btn, state){
29918        if(state){
29919            var g = groups[btn.toggleGroup];
29920            for(var i = 0, l = g.length; i < l; i++){
29921                if(g[i] != btn){
29922                    g[i].toggle(false);
29923                }
29924            }
29925        }
29926    }
29927    
29928    return {
29929        register : function(btn){
29930            if(!btn.toggleGroup){
29931                return;
29932            }
29933            var g = groups[btn.toggleGroup];
29934            if(!g){
29935                g = groups[btn.toggleGroup] = [];
29936            }
29937            g.push(btn);
29938            btn.on("toggle", toggleGroup);
29939        },
29940        
29941        unregister : function(btn){
29942            if(!btn.toggleGroup){
29943                return;
29944            }
29945            var g = groups[btn.toggleGroup];
29946            if(g){
29947                g.remove(btn);
29948                btn.un("toggle", toggleGroup);
29949            }
29950        }
29951    };
29952 }();/*
29953  * Based on:
29954  * Ext JS Library 1.1.1
29955  * Copyright(c) 2006-2007, Ext JS, LLC.
29956  *
29957  * Originally Released Under LGPL - original licence link has changed is not relivant.
29958  *
29959  * Fork - LGPL
29960  * <script type="text/javascript">
29961  */
29962  
29963 /**
29964  * @class Roo.SplitButton
29965  * @extends Roo.Button
29966  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29967  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29968  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29969  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29970  * @cfg {String} arrowTooltip The title attribute of the arrow
29971  * @constructor
29972  * Create a new menu button
29973  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29974  * @param {Object} config The config object
29975  */
29976 Roo.SplitButton = function(renderTo, config){
29977     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29978     /**
29979      * @event arrowclick
29980      * Fires when this button's arrow is clicked
29981      * @param {SplitButton} this
29982      * @param {EventObject} e The click event
29983      */
29984     this.addEvents({"arrowclick":true});
29985 };
29986
29987 Roo.extend(Roo.SplitButton, Roo.Button, {
29988     render : function(renderTo){
29989         // this is one sweet looking template!
29990         var tpl = new Roo.Template(
29991             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29992             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29993             '<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>',
29994             "</tbody></table></td><td>",
29995             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29996             '<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>',
29997             "</tbody></table></td></tr></table>"
29998         );
29999         var btn = tpl.append(renderTo, [this.text, this.type], true);
30000         var btnEl = btn.child("button");
30001         if(this.cls){
30002             btn.addClass(this.cls);
30003         }
30004         if(this.icon){
30005             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30006         }
30007         if(this.iconCls){
30008             btnEl.addClass(this.iconCls);
30009             if(!this.cls){
30010                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30011             }
30012         }
30013         this.el = btn;
30014         if(this.handleMouseEvents){
30015             btn.on("mouseover", this.onMouseOver, this);
30016             btn.on("mouseout", this.onMouseOut, this);
30017             btn.on("mousedown", this.onMouseDown, this);
30018             btn.on("mouseup", this.onMouseUp, this);
30019         }
30020         btn.on(this.clickEvent, this.onClick, this);
30021         if(this.tooltip){
30022             if(typeof this.tooltip == 'object'){
30023                 Roo.QuickTips.tips(Roo.apply({
30024                       target: btnEl.id
30025                 }, this.tooltip));
30026             } else {
30027                 btnEl.dom[this.tooltipType] = this.tooltip;
30028             }
30029         }
30030         if(this.arrowTooltip){
30031             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30032         }
30033         if(this.hidden){
30034             this.hide();
30035         }
30036         if(this.disabled){
30037             this.disable();
30038         }
30039         if(this.pressed){
30040             this.el.addClass("x-btn-pressed");
30041         }
30042         if(Roo.isIE && !Roo.isIE7){
30043             this.autoWidth.defer(1, this);
30044         }else{
30045             this.autoWidth();
30046         }
30047         if(this.menu){
30048             this.menu.on("show", this.onMenuShow, this);
30049             this.menu.on("hide", this.onMenuHide, this);
30050         }
30051         this.fireEvent('render', this);
30052     },
30053
30054     // private
30055     autoWidth : function(){
30056         if(this.el){
30057             var tbl = this.el.child("table:first");
30058             var tbl2 = this.el.child("table:last");
30059             this.el.setWidth("auto");
30060             tbl.setWidth("auto");
30061             if(Roo.isIE7 && Roo.isStrict){
30062                 var ib = this.el.child('button:first');
30063                 if(ib && ib.getWidth() > 20){
30064                     ib.clip();
30065                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30066                 }
30067             }
30068             if(this.minWidth){
30069                 if(this.hidden){
30070                     this.el.beginMeasure();
30071                 }
30072                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30073                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30074                 }
30075                 if(this.hidden){
30076                     this.el.endMeasure();
30077                 }
30078             }
30079             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30080         } 
30081     },
30082     /**
30083      * Sets this button's click handler
30084      * @param {Function} handler The function to call when the button is clicked
30085      * @param {Object} scope (optional) Scope for the function passed above
30086      */
30087     setHandler : function(handler, scope){
30088         this.handler = handler;
30089         this.scope = scope;  
30090     },
30091     
30092     /**
30093      * Sets this button's arrow click handler
30094      * @param {Function} handler The function to call when the arrow is clicked
30095      * @param {Object} scope (optional) Scope for the function passed above
30096      */
30097     setArrowHandler : function(handler, scope){
30098         this.arrowHandler = handler;
30099         this.scope = scope;  
30100     },
30101     
30102     /**
30103      * Focus the button
30104      */
30105     focus : function(){
30106         if(this.el){
30107             this.el.child("button:first").focus();
30108         }
30109     },
30110
30111     // private
30112     onClick : function(e){
30113         e.preventDefault();
30114         if(!this.disabled){
30115             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30116                 if(this.menu && !this.menu.isVisible()){
30117                     this.menu.show(this.el, this.menuAlign);
30118                 }
30119                 this.fireEvent("arrowclick", this, e);
30120                 if(this.arrowHandler){
30121                     this.arrowHandler.call(this.scope || this, this, e);
30122                 }
30123             }else{
30124                 this.fireEvent("click", this, e);
30125                 if(this.handler){
30126                     this.handler.call(this.scope || this, this, e);
30127                 }
30128             }
30129         }
30130     },
30131     // private
30132     onMouseDown : function(e){
30133         if(!this.disabled){
30134             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30135         }
30136     },
30137     // private
30138     onMouseUp : function(e){
30139         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30140     }   
30141 });
30142
30143
30144 // backwards compat
30145 Roo.MenuButton = Roo.SplitButton;/*
30146  * Based on:
30147  * Ext JS Library 1.1.1
30148  * Copyright(c) 2006-2007, Ext JS, LLC.
30149  *
30150  * Originally Released Under LGPL - original licence link has changed is not relivant.
30151  *
30152  * Fork - LGPL
30153  * <script type="text/javascript">
30154  */
30155
30156 /**
30157  * @class Roo.Toolbar
30158  * @children   Roo.Toolbar.Item Roo.form.Field
30159  * Basic Toolbar class.
30160  * @constructor
30161  * Creates a new Toolbar
30162  * @param {Object} container The config object
30163  */ 
30164 Roo.Toolbar = function(container, buttons, config)
30165 {
30166     /// old consturctor format still supported..
30167     if(container instanceof Array){ // omit the container for later rendering
30168         buttons = container;
30169         config = buttons;
30170         container = null;
30171     }
30172     if (typeof(container) == 'object' && container.xtype) {
30173         config = container;
30174         container = config.container;
30175         buttons = config.buttons || []; // not really - use items!!
30176     }
30177     var xitems = [];
30178     if (config && config.items) {
30179         xitems = config.items;
30180         delete config.items;
30181     }
30182     Roo.apply(this, config);
30183     this.buttons = buttons;
30184     
30185     if(container){
30186         this.render(container);
30187     }
30188     this.xitems = xitems;
30189     Roo.each(xitems, function(b) {
30190         this.add(b);
30191     }, this);
30192     
30193 };
30194
30195 Roo.Toolbar.prototype = {
30196     /**
30197      * @cfg {Array} items
30198      * array of button configs or elements to add (will be converted to a MixedCollection)
30199      */
30200     items: false,
30201     /**
30202      * @cfg {String/HTMLElement/Element} container
30203      * The id or element that will contain the toolbar
30204      */
30205     // private
30206     render : function(ct){
30207         this.el = Roo.get(ct);
30208         if(this.cls){
30209             this.el.addClass(this.cls);
30210         }
30211         // using a table allows for vertical alignment
30212         // 100% width is needed by Safari...
30213         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30214         this.tr = this.el.child("tr", true);
30215         var autoId = 0;
30216         this.items = new Roo.util.MixedCollection(false, function(o){
30217             return o.id || ("item" + (++autoId));
30218         });
30219         if(this.buttons){
30220             this.add.apply(this, this.buttons);
30221             delete this.buttons;
30222         }
30223     },
30224
30225     /**
30226      * Adds element(s) to the toolbar -- this function takes a variable number of 
30227      * arguments of mixed type and adds them to the toolbar.
30228      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30229      * <ul>
30230      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30231      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30232      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30233      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30234      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30235      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30236      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30237      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30238      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30239      * </ul>
30240      * @param {Mixed} arg2
30241      * @param {Mixed} etc.
30242      */
30243     add : function(){
30244         var a = arguments, l = a.length;
30245         for(var i = 0; i < l; i++){
30246             this._add(a[i]);
30247         }
30248     },
30249     // private..
30250     _add : function(el) {
30251         
30252         if (el.xtype) {
30253             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30254         }
30255         
30256         if (el.applyTo){ // some kind of form field
30257             return this.addField(el);
30258         } 
30259         if (el.render){ // some kind of Toolbar.Item
30260             return this.addItem(el);
30261         }
30262         if (typeof el == "string"){ // string
30263             if(el == "separator" || el == "-"){
30264                 return this.addSeparator();
30265             }
30266             if (el == " "){
30267                 return this.addSpacer();
30268             }
30269             if(el == "->"){
30270                 return this.addFill();
30271             }
30272             return this.addText(el);
30273             
30274         }
30275         if(el.tagName){ // element
30276             return this.addElement(el);
30277         }
30278         if(typeof el == "object"){ // must be button config?
30279             return this.addButton(el);
30280         }
30281         // and now what?!?!
30282         return false;
30283         
30284     },
30285     
30286     /**
30287      * Add an Xtype element
30288      * @param {Object} xtype Xtype Object
30289      * @return {Object} created Object
30290      */
30291     addxtype : function(e){
30292         return this.add(e);  
30293     },
30294     
30295     /**
30296      * Returns the Element for this toolbar.
30297      * @return {Roo.Element}
30298      */
30299     getEl : function(){
30300         return this.el;  
30301     },
30302     
30303     /**
30304      * Adds a separator
30305      * @return {Roo.Toolbar.Item} The separator item
30306      */
30307     addSeparator : function(){
30308         return this.addItem(new Roo.Toolbar.Separator());
30309     },
30310
30311     /**
30312      * Adds a spacer element
30313      * @return {Roo.Toolbar.Spacer} The spacer item
30314      */
30315     addSpacer : function(){
30316         return this.addItem(new Roo.Toolbar.Spacer());
30317     },
30318
30319     /**
30320      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30321      * @return {Roo.Toolbar.Fill} The fill item
30322      */
30323     addFill : function(){
30324         return this.addItem(new Roo.Toolbar.Fill());
30325     },
30326
30327     /**
30328      * Adds any standard HTML element to the toolbar
30329      * @param {String/HTMLElement/Element} el The element or id of the element to add
30330      * @return {Roo.Toolbar.Item} The element's item
30331      */
30332     addElement : function(el){
30333         return this.addItem(new Roo.Toolbar.Item(el));
30334     },
30335     /**
30336      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30337      * @type Roo.util.MixedCollection  
30338      */
30339     items : false,
30340      
30341     /**
30342      * Adds any Toolbar.Item or subclass
30343      * @param {Roo.Toolbar.Item} item
30344      * @return {Roo.Toolbar.Item} The item
30345      */
30346     addItem : function(item){
30347         var td = this.nextBlock();
30348         item.render(td);
30349         this.items.add(item);
30350         return item;
30351     },
30352     
30353     /**
30354      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30355      * @param {Object/Array} config A button config or array of configs
30356      * @return {Roo.Toolbar.Button/Array}
30357      */
30358     addButton : function(config){
30359         if(config instanceof Array){
30360             var buttons = [];
30361             for(var i = 0, len = config.length; i < len; i++) {
30362                 buttons.push(this.addButton(config[i]));
30363             }
30364             return buttons;
30365         }
30366         var b = config;
30367         if(!(config instanceof Roo.Toolbar.Button)){
30368             b = config.split ?
30369                 new Roo.Toolbar.SplitButton(config) :
30370                 new Roo.Toolbar.Button(config);
30371         }
30372         var td = this.nextBlock();
30373         b.render(td);
30374         this.items.add(b);
30375         return b;
30376     },
30377     
30378     /**
30379      * Adds text to the toolbar
30380      * @param {String} text The text to add
30381      * @return {Roo.Toolbar.Item} The element's item
30382      */
30383     addText : function(text){
30384         return this.addItem(new Roo.Toolbar.TextItem(text));
30385     },
30386     
30387     /**
30388      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30389      * @param {Number} index The index where the item is to be inserted
30390      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30391      * @return {Roo.Toolbar.Button/Item}
30392      */
30393     insertButton : function(index, item){
30394         if(item instanceof Array){
30395             var buttons = [];
30396             for(var i = 0, len = item.length; i < len; i++) {
30397                buttons.push(this.insertButton(index + i, item[i]));
30398             }
30399             return buttons;
30400         }
30401         if (!(item instanceof Roo.Toolbar.Button)){
30402            item = new Roo.Toolbar.Button(item);
30403         }
30404         var td = document.createElement("td");
30405         this.tr.insertBefore(td, this.tr.childNodes[index]);
30406         item.render(td);
30407         this.items.insert(index, item);
30408         return item;
30409     },
30410     
30411     /**
30412      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30413      * @param {Object} config
30414      * @return {Roo.Toolbar.Item} The element's item
30415      */
30416     addDom : function(config, returnEl){
30417         var td = this.nextBlock();
30418         Roo.DomHelper.overwrite(td, config);
30419         var ti = new Roo.Toolbar.Item(td.firstChild);
30420         ti.render(td);
30421         this.items.add(ti);
30422         return ti;
30423     },
30424
30425     /**
30426      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30427      * @type Roo.util.MixedCollection  
30428      */
30429     fields : false,
30430     
30431     /**
30432      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30433      * Note: the field should not have been rendered yet. For a field that has already been
30434      * rendered, use {@link #addElement}.
30435      * @param {Roo.form.Field} field
30436      * @return {Roo.ToolbarItem}
30437      */
30438      
30439       
30440     addField : function(field) {
30441         if (!this.fields) {
30442             var autoId = 0;
30443             this.fields = new Roo.util.MixedCollection(false, function(o){
30444                 return o.id || ("item" + (++autoId));
30445             });
30446
30447         }
30448         
30449         var td = this.nextBlock();
30450         field.render(td);
30451         var ti = new Roo.Toolbar.Item(td.firstChild);
30452         ti.render(td);
30453         this.items.add(ti);
30454         this.fields.add(field);
30455         return ti;
30456     },
30457     /**
30458      * Hide the toolbar
30459      * @method hide
30460      */
30461      
30462       
30463     hide : function()
30464     {
30465         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30466         this.el.child('div').hide();
30467     },
30468     /**
30469      * Show the toolbar
30470      * @method show
30471      */
30472     show : function()
30473     {
30474         this.el.child('div').show();
30475     },
30476       
30477     // private
30478     nextBlock : function(){
30479         var td = document.createElement("td");
30480         this.tr.appendChild(td);
30481         return td;
30482     },
30483
30484     // private
30485     destroy : function(){
30486         if(this.items){ // rendered?
30487             Roo.destroy.apply(Roo, this.items.items);
30488         }
30489         if(this.fields){ // rendered?
30490             Roo.destroy.apply(Roo, this.fields.items);
30491         }
30492         Roo.Element.uncache(this.el, this.tr);
30493     }
30494 };
30495
30496 /**
30497  * @class Roo.Toolbar.Item
30498  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30499  * @constructor
30500  * Creates a new Item
30501  * @param {HTMLElement} el 
30502  */
30503 Roo.Toolbar.Item = function(el){
30504     var cfg = {};
30505     if (typeof (el.xtype) != 'undefined') {
30506         cfg = el;
30507         el = cfg.el;
30508     }
30509     
30510     this.el = Roo.getDom(el);
30511     this.id = Roo.id(this.el);
30512     this.hidden = false;
30513     
30514     this.addEvents({
30515          /**
30516              * @event render
30517              * Fires when the button is rendered
30518              * @param {Button} this
30519              */
30520         'render': true
30521     });
30522     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30523 };
30524 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30525 //Roo.Toolbar.Item.prototype = {
30526     
30527     /**
30528      * Get this item's HTML Element
30529      * @return {HTMLElement}
30530      */
30531     getEl : function(){
30532        return this.el;  
30533     },
30534
30535     // private
30536     render : function(td){
30537         
30538          this.td = td;
30539         td.appendChild(this.el);
30540         
30541         this.fireEvent('render', this);
30542     },
30543     
30544     /**
30545      * Removes and destroys this item.
30546      */
30547     destroy : function(){
30548         this.td.parentNode.removeChild(this.td);
30549     },
30550     
30551     /**
30552      * Shows this item.
30553      */
30554     show: function(){
30555         this.hidden = false;
30556         this.td.style.display = "";
30557     },
30558     
30559     /**
30560      * Hides this item.
30561      */
30562     hide: function(){
30563         this.hidden = true;
30564         this.td.style.display = "none";
30565     },
30566     
30567     /**
30568      * Convenience function for boolean show/hide.
30569      * @param {Boolean} visible true to show/false to hide
30570      */
30571     setVisible: function(visible){
30572         if(visible) {
30573             this.show();
30574         }else{
30575             this.hide();
30576         }
30577     },
30578     
30579     /**
30580      * Try to focus this item.
30581      */
30582     focus : function(){
30583         Roo.fly(this.el).focus();
30584     },
30585     
30586     /**
30587      * Disables this item.
30588      */
30589     disable : function(){
30590         Roo.fly(this.td).addClass("x-item-disabled");
30591         this.disabled = true;
30592         this.el.disabled = true;
30593     },
30594     
30595     /**
30596      * Enables this item.
30597      */
30598     enable : function(){
30599         Roo.fly(this.td).removeClass("x-item-disabled");
30600         this.disabled = false;
30601         this.el.disabled = false;
30602     }
30603 });
30604
30605
30606 /**
30607  * @class Roo.Toolbar.Separator
30608  * @extends Roo.Toolbar.Item
30609  * A simple toolbar separator class
30610  * @constructor
30611  * Creates a new Separator
30612  */
30613 Roo.Toolbar.Separator = function(cfg){
30614     
30615     var s = document.createElement("span");
30616     s.className = "ytb-sep";
30617     if (cfg) {
30618         cfg.el = s;
30619     }
30620     
30621     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30622 };
30623 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30624     enable:Roo.emptyFn,
30625     disable:Roo.emptyFn,
30626     focus:Roo.emptyFn
30627 });
30628
30629 /**
30630  * @class Roo.Toolbar.Spacer
30631  * @extends Roo.Toolbar.Item
30632  * A simple element that adds extra horizontal space to a toolbar.
30633  * @constructor
30634  * Creates a new Spacer
30635  */
30636 Roo.Toolbar.Spacer = function(cfg){
30637     var s = document.createElement("div");
30638     s.className = "ytb-spacer";
30639     if (cfg) {
30640         cfg.el = s;
30641     }
30642     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30643 };
30644 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30645     enable:Roo.emptyFn,
30646     disable:Roo.emptyFn,
30647     focus:Roo.emptyFn
30648 });
30649
30650 /**
30651  * @class Roo.Toolbar.Fill
30652  * @extends Roo.Toolbar.Spacer
30653  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30654  * @constructor
30655  * Creates a new Spacer
30656  */
30657 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30658     // private
30659     render : function(td){
30660         td.style.width = '100%';
30661         Roo.Toolbar.Fill.superclass.render.call(this, td);
30662     }
30663 });
30664
30665 /**
30666  * @class Roo.Toolbar.TextItem
30667  * @extends Roo.Toolbar.Item
30668  * A simple class that renders text directly into a toolbar.
30669  * @constructor
30670  * Creates a new TextItem
30671  * @cfg {string} text 
30672  */
30673 Roo.Toolbar.TextItem = function(cfg){
30674     var  text = cfg || "";
30675     if (typeof(cfg) == 'object') {
30676         text = cfg.text || "";
30677     }  else {
30678         cfg = null;
30679     }
30680     var s = document.createElement("span");
30681     s.className = "ytb-text";
30682     s.innerHTML = text;
30683     if (cfg) {
30684         cfg.el  = s;
30685     }
30686     
30687     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30688 };
30689 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30690     
30691      
30692     enable:Roo.emptyFn,
30693     disable:Roo.emptyFn,
30694     focus:Roo.emptyFn
30695 });
30696
30697 /**
30698  * @class Roo.Toolbar.Button
30699  * @extends Roo.Button
30700  * A button that renders into a toolbar.
30701  * @constructor
30702  * Creates a new Button
30703  * @param {Object} config A standard {@link Roo.Button} config object
30704  */
30705 Roo.Toolbar.Button = function(config){
30706     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30707 };
30708 Roo.extend(Roo.Toolbar.Button, Roo.Button,
30709 {
30710     
30711     
30712     render : function(td){
30713         this.td = td;
30714         Roo.Toolbar.Button.superclass.render.call(this, td);
30715     },
30716     
30717     /**
30718      * Removes and destroys this button
30719      */
30720     destroy : function(){
30721         Roo.Toolbar.Button.superclass.destroy.call(this);
30722         this.td.parentNode.removeChild(this.td);
30723     },
30724     
30725     /**
30726      * Shows this button
30727      */
30728     show: function(){
30729         this.hidden = false;
30730         this.td.style.display = "";
30731     },
30732     
30733     /**
30734      * Hides this button
30735      */
30736     hide: function(){
30737         this.hidden = true;
30738         this.td.style.display = "none";
30739     },
30740
30741     /**
30742      * Disables this item
30743      */
30744     disable : function(){
30745         Roo.fly(this.td).addClass("x-item-disabled");
30746         this.disabled = true;
30747     },
30748
30749     /**
30750      * Enables this item
30751      */
30752     enable : function(){
30753         Roo.fly(this.td).removeClass("x-item-disabled");
30754         this.disabled = false;
30755     }
30756 });
30757 // backwards compat
30758 Roo.ToolbarButton = Roo.Toolbar.Button;
30759
30760 /**
30761  * @class Roo.Toolbar.SplitButton
30762  * @extends Roo.SplitButton
30763  * A menu button that renders into a toolbar.
30764  * @constructor
30765  * Creates a new SplitButton
30766  * @param {Object} config A standard {@link Roo.SplitButton} config object
30767  */
30768 Roo.Toolbar.SplitButton = function(config){
30769     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30770 };
30771 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30772     render : function(td){
30773         this.td = td;
30774         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30775     },
30776     
30777     /**
30778      * Removes and destroys this button
30779      */
30780     destroy : function(){
30781         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30782         this.td.parentNode.removeChild(this.td);
30783     },
30784     
30785     /**
30786      * Shows this button
30787      */
30788     show: function(){
30789         this.hidden = false;
30790         this.td.style.display = "";
30791     },
30792     
30793     /**
30794      * Hides this button
30795      */
30796     hide: function(){
30797         this.hidden = true;
30798         this.td.style.display = "none";
30799     }
30800 });
30801
30802 // backwards compat
30803 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30804  * Based on:
30805  * Ext JS Library 1.1.1
30806  * Copyright(c) 2006-2007, Ext JS, LLC.
30807  *
30808  * Originally Released Under LGPL - original licence link has changed is not relivant.
30809  *
30810  * Fork - LGPL
30811  * <script type="text/javascript">
30812  */
30813  
30814 /**
30815  * @class Roo.PagingToolbar
30816  * @extends Roo.Toolbar
30817  * @children   Roo.Toolbar.Item Roo.form.Field
30818  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30819  * @constructor
30820  * Create a new PagingToolbar
30821  * @param {Object} config The config object
30822  */
30823 Roo.PagingToolbar = function(el, ds, config)
30824 {
30825     // old args format still supported... - xtype is prefered..
30826     if (typeof(el) == 'object' && el.xtype) {
30827         // created from xtype...
30828         config = el;
30829         ds = el.dataSource;
30830         el = config.container;
30831     }
30832     var items = [];
30833     if (config.items) {
30834         items = config.items;
30835         config.items = [];
30836     }
30837     
30838     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30839     this.ds = ds;
30840     this.cursor = 0;
30841     this.renderButtons(this.el);
30842     this.bind(ds);
30843     
30844     // supprot items array.
30845    
30846     Roo.each(items, function(e) {
30847         this.add(Roo.factory(e));
30848     },this);
30849     
30850 };
30851
30852 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30853     /**
30854      * @cfg {Roo.data.Store} dataSource
30855      * The underlying data store providing the paged data
30856      */
30857     /**
30858      * @cfg {String/HTMLElement/Element} container
30859      * container The id or element that will contain the toolbar
30860      */
30861     /**
30862      * @cfg {Boolean} displayInfo
30863      * True to display the displayMsg (defaults to false)
30864      */
30865     /**
30866      * @cfg {Number} pageSize
30867      * The number of records to display per page (defaults to 20)
30868      */
30869     pageSize: 20,
30870     /**
30871      * @cfg {String} displayMsg
30872      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30873      */
30874     displayMsg : 'Displaying {0} - {1} of {2}',
30875     /**
30876      * @cfg {String} emptyMsg
30877      * The message to display when no records are found (defaults to "No data to display")
30878      */
30879     emptyMsg : 'No data to display',
30880     /**
30881      * Customizable piece of the default paging text (defaults to "Page")
30882      * @type String
30883      */
30884     beforePageText : "Page",
30885     /**
30886      * Customizable piece of the default paging text (defaults to "of %0")
30887      * @type String
30888      */
30889     afterPageText : "of {0}",
30890     /**
30891      * Customizable piece of the default paging text (defaults to "First Page")
30892      * @type String
30893      */
30894     firstText : "First Page",
30895     /**
30896      * Customizable piece of the default paging text (defaults to "Previous Page")
30897      * @type String
30898      */
30899     prevText : "Previous Page",
30900     /**
30901      * Customizable piece of the default paging text (defaults to "Next Page")
30902      * @type String
30903      */
30904     nextText : "Next Page",
30905     /**
30906      * Customizable piece of the default paging text (defaults to "Last Page")
30907      * @type String
30908      */
30909     lastText : "Last Page",
30910     /**
30911      * Customizable piece of the default paging text (defaults to "Refresh")
30912      * @type String
30913      */
30914     refreshText : "Refresh",
30915
30916     // private
30917     renderButtons : function(el){
30918         Roo.PagingToolbar.superclass.render.call(this, el);
30919         this.first = this.addButton({
30920             tooltip: this.firstText,
30921             cls: "x-btn-icon x-grid-page-first",
30922             disabled: true,
30923             handler: this.onClick.createDelegate(this, ["first"])
30924         });
30925         this.prev = this.addButton({
30926             tooltip: this.prevText,
30927             cls: "x-btn-icon x-grid-page-prev",
30928             disabled: true,
30929             handler: this.onClick.createDelegate(this, ["prev"])
30930         });
30931         //this.addSeparator();
30932         this.add(this.beforePageText);
30933         this.field = Roo.get(this.addDom({
30934            tag: "input",
30935            type: "text",
30936            size: "3",
30937            value: "1",
30938            cls: "x-grid-page-number"
30939         }).el);
30940         this.field.on("keydown", this.onPagingKeydown, this);
30941         this.field.on("focus", function(){this.dom.select();});
30942         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30943         this.field.setHeight(18);
30944         //this.addSeparator();
30945         this.next = this.addButton({
30946             tooltip: this.nextText,
30947             cls: "x-btn-icon x-grid-page-next",
30948             disabled: true,
30949             handler: this.onClick.createDelegate(this, ["next"])
30950         });
30951         this.last = this.addButton({
30952             tooltip: this.lastText,
30953             cls: "x-btn-icon x-grid-page-last",
30954             disabled: true,
30955             handler: this.onClick.createDelegate(this, ["last"])
30956         });
30957         //this.addSeparator();
30958         this.loading = this.addButton({
30959             tooltip: this.refreshText,
30960             cls: "x-btn-icon x-grid-loading",
30961             handler: this.onClick.createDelegate(this, ["refresh"])
30962         });
30963
30964         if(this.displayInfo){
30965             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30966         }
30967     },
30968
30969     // private
30970     updateInfo : function(){
30971         if(this.displayEl){
30972             var count = this.ds.getCount();
30973             var msg = count == 0 ?
30974                 this.emptyMsg :
30975                 String.format(
30976                     this.displayMsg,
30977                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30978                 );
30979             this.displayEl.update(msg);
30980         }
30981     },
30982
30983     // private
30984     onLoad : function(ds, r, o){
30985        this.cursor = o.params ? o.params.start : 0;
30986        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30987
30988        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30989        this.field.dom.value = ap;
30990        this.first.setDisabled(ap == 1);
30991        this.prev.setDisabled(ap == 1);
30992        this.next.setDisabled(ap == ps);
30993        this.last.setDisabled(ap == ps);
30994        this.loading.enable();
30995        this.updateInfo();
30996     },
30997
30998     // private
30999     getPageData : function(){
31000         var total = this.ds.getTotalCount();
31001         return {
31002             total : total,
31003             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31004             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31005         };
31006     },
31007
31008     // private
31009     onLoadError : function(){
31010         this.loading.enable();
31011     },
31012
31013     // private
31014     onPagingKeydown : function(e){
31015         var k = e.getKey();
31016         var d = this.getPageData();
31017         if(k == e.RETURN){
31018             var v = this.field.dom.value, pageNum;
31019             if(!v || isNaN(pageNum = parseInt(v, 10))){
31020                 this.field.dom.value = d.activePage;
31021                 return;
31022             }
31023             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31024             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31025             e.stopEvent();
31026         }
31027         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))
31028         {
31029           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31030           this.field.dom.value = pageNum;
31031           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31032           e.stopEvent();
31033         }
31034         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31035         {
31036           var v = this.field.dom.value, pageNum; 
31037           var increment = (e.shiftKey) ? 10 : 1;
31038           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31039             increment *= -1;
31040           }
31041           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31042             this.field.dom.value = d.activePage;
31043             return;
31044           }
31045           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31046           {
31047             this.field.dom.value = parseInt(v, 10) + increment;
31048             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31049             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31050           }
31051           e.stopEvent();
31052         }
31053     },
31054
31055     // private
31056     beforeLoad : function(){
31057         if(this.loading){
31058             this.loading.disable();
31059         }
31060     },
31061
31062     // private
31063     onClick : function(which){
31064         var ds = this.ds;
31065         switch(which){
31066             case "first":
31067                 ds.load({params:{start: 0, limit: this.pageSize}});
31068             break;
31069             case "prev":
31070                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31071             break;
31072             case "next":
31073                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31074             break;
31075             case "last":
31076                 var total = ds.getTotalCount();
31077                 var extra = total % this.pageSize;
31078                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31079                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31080             break;
31081             case "refresh":
31082                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31083             break;
31084         }
31085     },
31086
31087     /**
31088      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31089      * @param {Roo.data.Store} store The data store to unbind
31090      */
31091     unbind : function(ds){
31092         ds.un("beforeload", this.beforeLoad, this);
31093         ds.un("load", this.onLoad, this);
31094         ds.un("loadexception", this.onLoadError, this);
31095         ds.un("remove", this.updateInfo, this);
31096         ds.un("add", this.updateInfo, this);
31097         this.ds = undefined;
31098     },
31099
31100     /**
31101      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31102      * @param {Roo.data.Store} store The data store to bind
31103      */
31104     bind : function(ds){
31105         ds.on("beforeload", this.beforeLoad, this);
31106         ds.on("load", this.onLoad, this);
31107         ds.on("loadexception", this.onLoadError, this);
31108         ds.on("remove", this.updateInfo, this);
31109         ds.on("add", this.updateInfo, this);
31110         this.ds = ds;
31111     }
31112 });/*
31113  * Based on:
31114  * Ext JS Library 1.1.1
31115  * Copyright(c) 2006-2007, Ext JS, LLC.
31116  *
31117  * Originally Released Under LGPL - original licence link has changed is not relivant.
31118  *
31119  * Fork - LGPL
31120  * <script type="text/javascript">
31121  */
31122
31123 /**
31124  * @class Roo.Resizable
31125  * @extends Roo.util.Observable
31126  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31127  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31128  * 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
31129  * the element will be wrapped for you automatically.</p>
31130  * <p>Here is the list of valid resize handles:</p>
31131  * <pre>
31132 Value   Description
31133 ------  -------------------
31134  'n'     north
31135  's'     south
31136  'e'     east
31137  'w'     west
31138  'nw'    northwest
31139  'sw'    southwest
31140  'se'    southeast
31141  'ne'    northeast
31142  'hd'    horizontal drag
31143  'all'   all
31144 </pre>
31145  * <p>Here's an example showing the creation of a typical Resizable:</p>
31146  * <pre><code>
31147 var resizer = new Roo.Resizable("element-id", {
31148     handles: 'all',
31149     minWidth: 200,
31150     minHeight: 100,
31151     maxWidth: 500,
31152     maxHeight: 400,
31153     pinned: true
31154 });
31155 resizer.on("resize", myHandler);
31156 </code></pre>
31157  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31158  * resizer.east.setDisplayed(false);</p>
31159  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31160  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31161  * resize operation's new size (defaults to [0, 0])
31162  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31163  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31164  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31165  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31166  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31167  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31168  * @cfg {Number} width The width of the element in pixels (defaults to null)
31169  * @cfg {Number} height The height of the element in pixels (defaults to null)
31170  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31171  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31172  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31173  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31174  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31175  * in favor of the handles config option (defaults to false)
31176  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31177  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31178  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31179  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31180  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31181  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31182  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31183  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31184  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31185  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31186  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31187  * @constructor
31188  * Create a new resizable component
31189  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31190  * @param {Object} config configuration options
31191   */
31192 Roo.Resizable = function(el, config)
31193 {
31194     this.el = Roo.get(el);
31195
31196     if(config && config.wrap){
31197         config.resizeChild = this.el;
31198         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31199         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31200         this.el.setStyle("overflow", "hidden");
31201         this.el.setPositioning(config.resizeChild.getPositioning());
31202         config.resizeChild.clearPositioning();
31203         if(!config.width || !config.height){
31204             var csize = config.resizeChild.getSize();
31205             this.el.setSize(csize.width, csize.height);
31206         }
31207         if(config.pinned && !config.adjustments){
31208             config.adjustments = "auto";
31209         }
31210     }
31211
31212     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31213     this.proxy.unselectable();
31214     this.proxy.enableDisplayMode('block');
31215
31216     Roo.apply(this, config);
31217
31218     if(this.pinned){
31219         this.disableTrackOver = true;
31220         this.el.addClass("x-resizable-pinned");
31221     }
31222     // if the element isn't positioned, make it relative
31223     var position = this.el.getStyle("position");
31224     if(position != "absolute" && position != "fixed"){
31225         this.el.setStyle("position", "relative");
31226     }
31227     if(!this.handles){ // no handles passed, must be legacy style
31228         this.handles = 's,e,se';
31229         if(this.multiDirectional){
31230             this.handles += ',n,w';
31231         }
31232     }
31233     if(this.handles == "all"){
31234         this.handles = "n s e w ne nw se sw";
31235     }
31236     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31237     var ps = Roo.Resizable.positions;
31238     for(var i = 0, len = hs.length; i < len; i++){
31239         if(hs[i] && ps[hs[i]]){
31240             var pos = ps[hs[i]];
31241             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31242         }
31243     }
31244     // legacy
31245     this.corner = this.southeast;
31246     
31247     // updateBox = the box can move..
31248     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31249         this.updateBox = true;
31250     }
31251
31252     this.activeHandle = null;
31253
31254     if(this.resizeChild){
31255         if(typeof this.resizeChild == "boolean"){
31256             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31257         }else{
31258             this.resizeChild = Roo.get(this.resizeChild, true);
31259         }
31260     }
31261     
31262     if(this.adjustments == "auto"){
31263         var rc = this.resizeChild;
31264         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31265         if(rc && (hw || hn)){
31266             rc.position("relative");
31267             rc.setLeft(hw ? hw.el.getWidth() : 0);
31268             rc.setTop(hn ? hn.el.getHeight() : 0);
31269         }
31270         this.adjustments = [
31271             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31272             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31273         ];
31274     }
31275
31276     if(this.draggable){
31277         this.dd = this.dynamic ?
31278             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31279         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31280     }
31281
31282     // public events
31283     this.addEvents({
31284         /**
31285          * @event beforeresize
31286          * Fired before resize is allowed. Set enabled to false to cancel resize.
31287          * @param {Roo.Resizable} this
31288          * @param {Roo.EventObject} e The mousedown event
31289          */
31290         "beforeresize" : true,
31291         /**
31292          * @event resizing
31293          * Fired a resizing.
31294          * @param {Roo.Resizable} this
31295          * @param {Number} x The new x position
31296          * @param {Number} y The new y position
31297          * @param {Number} w The new w width
31298          * @param {Number} h The new h hight
31299          * @param {Roo.EventObject} e The mouseup event
31300          */
31301         "resizing" : true,
31302         /**
31303          * @event resize
31304          * Fired after a resize.
31305          * @param {Roo.Resizable} this
31306          * @param {Number} width The new width
31307          * @param {Number} height The new height
31308          * @param {Roo.EventObject} e The mouseup event
31309          */
31310         "resize" : true
31311     });
31312
31313     if(this.width !== null && this.height !== null){
31314         this.resizeTo(this.width, this.height);
31315     }else{
31316         this.updateChildSize();
31317     }
31318     if(Roo.isIE){
31319         this.el.dom.style.zoom = 1;
31320     }
31321     Roo.Resizable.superclass.constructor.call(this);
31322 };
31323
31324 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31325         resizeChild : false,
31326         adjustments : [0, 0],
31327         minWidth : 5,
31328         minHeight : 5,
31329         maxWidth : 10000,
31330         maxHeight : 10000,
31331         enabled : true,
31332         animate : false,
31333         duration : .35,
31334         dynamic : false,
31335         handles : false,
31336         multiDirectional : false,
31337         disableTrackOver : false,
31338         easing : 'easeOutStrong',
31339         widthIncrement : 0,
31340         heightIncrement : 0,
31341         pinned : false,
31342         width : null,
31343         height : null,
31344         preserveRatio : false,
31345         transparent: false,
31346         minX: 0,
31347         minY: 0,
31348         draggable: false,
31349
31350         /**
31351          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31352          */
31353         constrainTo: undefined,
31354         /**
31355          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31356          */
31357         resizeRegion: undefined,
31358
31359
31360     /**
31361      * Perform a manual resize
31362      * @param {Number} width
31363      * @param {Number} height
31364      */
31365     resizeTo : function(width, height){
31366         this.el.setSize(width, height);
31367         this.updateChildSize();
31368         this.fireEvent("resize", this, width, height, null);
31369     },
31370
31371     // private
31372     startSizing : function(e, handle){
31373         this.fireEvent("beforeresize", this, e);
31374         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31375
31376             if(!this.overlay){
31377                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31378                 this.overlay.unselectable();
31379                 this.overlay.enableDisplayMode("block");
31380                 this.overlay.on("mousemove", this.onMouseMove, this);
31381                 this.overlay.on("mouseup", this.onMouseUp, this);
31382             }
31383             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31384
31385             this.resizing = true;
31386             this.startBox = this.el.getBox();
31387             this.startPoint = e.getXY();
31388             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31389                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31390
31391             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31392             this.overlay.show();
31393
31394             if(this.constrainTo) {
31395                 var ct = Roo.get(this.constrainTo);
31396                 this.resizeRegion = ct.getRegion().adjust(
31397                     ct.getFrameWidth('t'),
31398                     ct.getFrameWidth('l'),
31399                     -ct.getFrameWidth('b'),
31400                     -ct.getFrameWidth('r')
31401                 );
31402             }
31403
31404             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31405             this.proxy.show();
31406             this.proxy.setBox(this.startBox);
31407             if(!this.dynamic){
31408                 this.proxy.setStyle('visibility', 'visible');
31409             }
31410         }
31411     },
31412
31413     // private
31414     onMouseDown : function(handle, e){
31415         if(this.enabled){
31416             e.stopEvent();
31417             this.activeHandle = handle;
31418             this.startSizing(e, handle);
31419         }
31420     },
31421
31422     // private
31423     onMouseUp : function(e){
31424         var size = this.resizeElement();
31425         this.resizing = false;
31426         this.handleOut();
31427         this.overlay.hide();
31428         this.proxy.hide();
31429         this.fireEvent("resize", this, size.width, size.height, e);
31430     },
31431
31432     // private
31433     updateChildSize : function(){
31434         
31435         if(this.resizeChild){
31436             var el = this.el;
31437             var child = this.resizeChild;
31438             var adj = this.adjustments;
31439             if(el.dom.offsetWidth){
31440                 var b = el.getSize(true);
31441                 child.setSize(b.width+adj[0], b.height+adj[1]);
31442             }
31443             // Second call here for IE
31444             // The first call enables instant resizing and
31445             // the second call corrects scroll bars if they
31446             // exist
31447             if(Roo.isIE){
31448                 setTimeout(function(){
31449                     if(el.dom.offsetWidth){
31450                         var b = el.getSize(true);
31451                         child.setSize(b.width+adj[0], b.height+adj[1]);
31452                     }
31453                 }, 10);
31454             }
31455         }
31456     },
31457
31458     // private
31459     snap : function(value, inc, min){
31460         if(!inc || !value) {
31461             return value;
31462         }
31463         var newValue = value;
31464         var m = value % inc;
31465         if(m > 0){
31466             if(m > (inc/2)){
31467                 newValue = value + (inc-m);
31468             }else{
31469                 newValue = value - m;
31470             }
31471         }
31472         return Math.max(min, newValue);
31473     },
31474
31475     // private
31476     resizeElement : function(){
31477         var box = this.proxy.getBox();
31478         if(this.updateBox){
31479             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31480         }else{
31481             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31482         }
31483         this.updateChildSize();
31484         if(!this.dynamic){
31485             this.proxy.hide();
31486         }
31487         return box;
31488     },
31489
31490     // private
31491     constrain : function(v, diff, m, mx){
31492         if(v - diff < m){
31493             diff = v - m;
31494         }else if(v - diff > mx){
31495             diff = mx - v;
31496         }
31497         return diff;
31498     },
31499
31500     // private
31501     onMouseMove : function(e){
31502         
31503         if(this.enabled){
31504             try{// try catch so if something goes wrong the user doesn't get hung
31505
31506             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31507                 return;
31508             }
31509
31510             //var curXY = this.startPoint;
31511             var curSize = this.curSize || this.startBox;
31512             var x = this.startBox.x, y = this.startBox.y;
31513             var ox = x, oy = y;
31514             var w = curSize.width, h = curSize.height;
31515             var ow = w, oh = h;
31516             var mw = this.minWidth, mh = this.minHeight;
31517             var mxw = this.maxWidth, mxh = this.maxHeight;
31518             var wi = this.widthIncrement;
31519             var hi = this.heightIncrement;
31520
31521             var eventXY = e.getXY();
31522             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31523             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31524
31525             var pos = this.activeHandle.position;
31526
31527             switch(pos){
31528                 case "east":
31529                     w += diffX;
31530                     w = Math.min(Math.max(mw, w), mxw);
31531                     break;
31532              
31533                 case "south":
31534                     h += diffY;
31535                     h = Math.min(Math.max(mh, h), mxh);
31536                     break;
31537                 case "southeast":
31538                     w += diffX;
31539                     h += diffY;
31540                     w = Math.min(Math.max(mw, w), mxw);
31541                     h = Math.min(Math.max(mh, h), mxh);
31542                     break;
31543                 case "north":
31544                     diffY = this.constrain(h, diffY, mh, mxh);
31545                     y += diffY;
31546                     h -= diffY;
31547                     break;
31548                 case "hdrag":
31549                     
31550                     if (wi) {
31551                         var adiffX = Math.abs(diffX);
31552                         var sub = (adiffX % wi); // how much 
31553                         if (sub > (wi/2)) { // far enough to snap
31554                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31555                         } else {
31556                             // remove difference.. 
31557                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31558                         }
31559                     }
31560                     x += diffX;
31561                     x = Math.max(this.minX, x);
31562                     break;
31563                 case "west":
31564                     diffX = this.constrain(w, diffX, mw, mxw);
31565                     x += diffX;
31566                     w -= diffX;
31567                     break;
31568                 case "northeast":
31569                     w += diffX;
31570                     w = Math.min(Math.max(mw, w), mxw);
31571                     diffY = this.constrain(h, diffY, mh, mxh);
31572                     y += diffY;
31573                     h -= diffY;
31574                     break;
31575                 case "northwest":
31576                     diffX = this.constrain(w, diffX, mw, mxw);
31577                     diffY = this.constrain(h, diffY, mh, mxh);
31578                     y += diffY;
31579                     h -= diffY;
31580                     x += diffX;
31581                     w -= diffX;
31582                     break;
31583                case "southwest":
31584                     diffX = this.constrain(w, diffX, mw, mxw);
31585                     h += diffY;
31586                     h = Math.min(Math.max(mh, h), mxh);
31587                     x += diffX;
31588                     w -= diffX;
31589                     break;
31590             }
31591
31592             var sw = this.snap(w, wi, mw);
31593             var sh = this.snap(h, hi, mh);
31594             if(sw != w || sh != h){
31595                 switch(pos){
31596                     case "northeast":
31597                         y -= sh - h;
31598                     break;
31599                     case "north":
31600                         y -= sh - h;
31601                         break;
31602                     case "southwest":
31603                         x -= sw - w;
31604                     break;
31605                     case "west":
31606                         x -= sw - w;
31607                         break;
31608                     case "northwest":
31609                         x -= sw - w;
31610                         y -= sh - h;
31611                     break;
31612                 }
31613                 w = sw;
31614                 h = sh;
31615             }
31616
31617             if(this.preserveRatio){
31618                 switch(pos){
31619                     case "southeast":
31620                     case "east":
31621                         h = oh * (w/ow);
31622                         h = Math.min(Math.max(mh, h), mxh);
31623                         w = ow * (h/oh);
31624                        break;
31625                     case "south":
31626                         w = ow * (h/oh);
31627                         w = Math.min(Math.max(mw, w), mxw);
31628                         h = oh * (w/ow);
31629                         break;
31630                     case "northeast":
31631                         w = ow * (h/oh);
31632                         w = Math.min(Math.max(mw, w), mxw);
31633                         h = oh * (w/ow);
31634                     break;
31635                     case "north":
31636                         var tw = w;
31637                         w = ow * (h/oh);
31638                         w = Math.min(Math.max(mw, w), mxw);
31639                         h = oh * (w/ow);
31640                         x += (tw - w) / 2;
31641                         break;
31642                     case "southwest":
31643                         h = oh * (w/ow);
31644                         h = Math.min(Math.max(mh, h), mxh);
31645                         var tw = w;
31646                         w = ow * (h/oh);
31647                         x += tw - w;
31648                         break;
31649                     case "west":
31650                         var th = h;
31651                         h = oh * (w/ow);
31652                         h = Math.min(Math.max(mh, h), mxh);
31653                         y += (th - h) / 2;
31654                         var tw = w;
31655                         w = ow * (h/oh);
31656                         x += tw - w;
31657                        break;
31658                     case "northwest":
31659                         var tw = w;
31660                         var th = h;
31661                         h = oh * (w/ow);
31662                         h = Math.min(Math.max(mh, h), mxh);
31663                         w = ow * (h/oh);
31664                         y += th - h;
31665                         x += tw - w;
31666                        break;
31667
31668                 }
31669             }
31670             if (pos == 'hdrag') {
31671                 w = ow;
31672             }
31673             this.proxy.setBounds(x, y, w, h);
31674             if(this.dynamic){
31675                 this.resizeElement();
31676             }
31677             }catch(e){}
31678         }
31679         this.fireEvent("resizing", this, x, y, w, h, e);
31680     },
31681
31682     // private
31683     handleOver : function(){
31684         if(this.enabled){
31685             this.el.addClass("x-resizable-over");
31686         }
31687     },
31688
31689     // private
31690     handleOut : function(){
31691         if(!this.resizing){
31692             this.el.removeClass("x-resizable-over");
31693         }
31694     },
31695
31696     /**
31697      * Returns the element this component is bound to.
31698      * @return {Roo.Element}
31699      */
31700     getEl : function(){
31701         return this.el;
31702     },
31703
31704     /**
31705      * Returns the resizeChild element (or null).
31706      * @return {Roo.Element}
31707      */
31708     getResizeChild : function(){
31709         return this.resizeChild;
31710     },
31711     groupHandler : function()
31712     {
31713         
31714     },
31715     /**
31716      * Destroys this resizable. If the element was wrapped and
31717      * removeEl is not true then the element remains.
31718      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31719      */
31720     destroy : function(removeEl){
31721         this.proxy.remove();
31722         if(this.overlay){
31723             this.overlay.removeAllListeners();
31724             this.overlay.remove();
31725         }
31726         var ps = Roo.Resizable.positions;
31727         for(var k in ps){
31728             if(typeof ps[k] != "function" && this[ps[k]]){
31729                 var h = this[ps[k]];
31730                 h.el.removeAllListeners();
31731                 h.el.remove();
31732             }
31733         }
31734         if(removeEl){
31735             this.el.update("");
31736             this.el.remove();
31737         }
31738     }
31739 });
31740
31741 // private
31742 // hash to map config positions to true positions
31743 Roo.Resizable.positions = {
31744     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31745     hd: "hdrag"
31746 };
31747
31748 // private
31749 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31750     if(!this.tpl){
31751         // only initialize the template if resizable is used
31752         var tpl = Roo.DomHelper.createTemplate(
31753             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31754         );
31755         tpl.compile();
31756         Roo.Resizable.Handle.prototype.tpl = tpl;
31757     }
31758     this.position = pos;
31759     this.rz = rz;
31760     // show north drag fro topdra
31761     var handlepos = pos == 'hdrag' ? 'north' : pos;
31762     
31763     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31764     if (pos == 'hdrag') {
31765         this.el.setStyle('cursor', 'pointer');
31766     }
31767     this.el.unselectable();
31768     if(transparent){
31769         this.el.setOpacity(0);
31770     }
31771     this.el.on("mousedown", this.onMouseDown, this);
31772     if(!disableTrackOver){
31773         this.el.on("mouseover", this.onMouseOver, this);
31774         this.el.on("mouseout", this.onMouseOut, this);
31775     }
31776 };
31777
31778 // private
31779 Roo.Resizable.Handle.prototype = {
31780     afterResize : function(rz){
31781         Roo.log('after?');
31782         // do nothing
31783     },
31784     // private
31785     onMouseDown : function(e){
31786         this.rz.onMouseDown(this, e);
31787     },
31788     // private
31789     onMouseOver : function(e){
31790         this.rz.handleOver(this, e);
31791     },
31792     // private
31793     onMouseOut : function(e){
31794         this.rz.handleOut(this, e);
31795     }
31796 };/*
31797  * Based on:
31798  * Ext JS Library 1.1.1
31799  * Copyright(c) 2006-2007, Ext JS, LLC.
31800  *
31801  * Originally Released Under LGPL - original licence link has changed is not relivant.
31802  *
31803  * Fork - LGPL
31804  * <script type="text/javascript">
31805  */
31806
31807 /**
31808  * @class Roo.Editor
31809  * @extends Roo.Component
31810  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31811  * @constructor
31812  * Create a new Editor
31813  * @param {Roo.form.Field} field The Field object (or descendant)
31814  * @param {Object} config The config object
31815  */
31816 Roo.Editor = function(field, config){
31817     Roo.Editor.superclass.constructor.call(this, config);
31818     this.field = field;
31819     this.addEvents({
31820         /**
31821              * @event beforestartedit
31822              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31823              * false from the handler of this event.
31824              * @param {Editor} this
31825              * @param {Roo.Element} boundEl The underlying element bound to this editor
31826              * @param {Mixed} value The field value being set
31827              */
31828         "beforestartedit" : true,
31829         /**
31830              * @event startedit
31831              * Fires when this editor is displayed
31832              * @param {Roo.Element} boundEl The underlying element bound to this editor
31833              * @param {Mixed} value The starting field value
31834              */
31835         "startedit" : true,
31836         /**
31837              * @event beforecomplete
31838              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31839              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31840              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31841              * event will not fire since no edit actually occurred.
31842              * @param {Editor} this
31843              * @param {Mixed} value The current field value
31844              * @param {Mixed} startValue The original field value
31845              */
31846         "beforecomplete" : true,
31847         /**
31848              * @event complete
31849              * Fires after editing is complete and any changed value has been written to the underlying field.
31850              * @param {Editor} this
31851              * @param {Mixed} value The current field value
31852              * @param {Mixed} startValue The original field value
31853              */
31854         "complete" : true,
31855         /**
31856          * @event specialkey
31857          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31858          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31859          * @param {Roo.form.Field} this
31860          * @param {Roo.EventObject} e The event object
31861          */
31862         "specialkey" : true
31863     });
31864 };
31865
31866 Roo.extend(Roo.Editor, Roo.Component, {
31867     /**
31868      * @cfg {Boolean/String} autosize
31869      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31870      * or "height" to adopt the height only (defaults to false)
31871      */
31872     /**
31873      * @cfg {Boolean} revertInvalid
31874      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31875      * validation fails (defaults to true)
31876      */
31877     /**
31878      * @cfg {Boolean} ignoreNoChange
31879      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31880      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31881      * will never be ignored.
31882      */
31883     /**
31884      * @cfg {Boolean} hideEl
31885      * False to keep the bound element visible while the editor is displayed (defaults to true)
31886      */
31887     /**
31888      * @cfg {Mixed} value
31889      * The data value of the underlying field (defaults to "")
31890      */
31891     value : "",
31892     /**
31893      * @cfg {String} alignment
31894      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31895      */
31896     alignment: "c-c?",
31897     /**
31898      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31899      * for bottom-right shadow (defaults to "frame")
31900      */
31901     shadow : "frame",
31902     /**
31903      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31904      */
31905     constrain : false,
31906     /**
31907      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31908      */
31909     completeOnEnter : false,
31910     /**
31911      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31912      */
31913     cancelOnEsc : false,
31914     /**
31915      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31916      */
31917     updateEl : false,
31918
31919     // private
31920     onRender : function(ct, position){
31921         this.el = new Roo.Layer({
31922             shadow: this.shadow,
31923             cls: "x-editor",
31924             parentEl : ct,
31925             shim : this.shim,
31926             shadowOffset:4,
31927             id: this.id,
31928             constrain: this.constrain
31929         });
31930         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31931         if(this.field.msgTarget != 'title'){
31932             this.field.msgTarget = 'qtip';
31933         }
31934         this.field.render(this.el);
31935         if(Roo.isGecko){
31936             this.field.el.dom.setAttribute('autocomplete', 'off');
31937         }
31938         this.field.on("specialkey", this.onSpecialKey, this);
31939         if(this.swallowKeys){
31940             this.field.el.swallowEvent(['keydown','keypress']);
31941         }
31942         this.field.show();
31943         this.field.on("blur", this.onBlur, this);
31944         if(this.field.grow){
31945             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31946         }
31947     },
31948
31949     onSpecialKey : function(field, e)
31950     {
31951         //Roo.log('editor onSpecialKey');
31952         if(this.completeOnEnter && e.getKey() == e.ENTER){
31953             e.stopEvent();
31954             this.completeEdit();
31955             return;
31956         }
31957         // do not fire special key otherwise it might hide close the editor...
31958         if(e.getKey() == e.ENTER){    
31959             return;
31960         }
31961         if(this.cancelOnEsc && e.getKey() == e.ESC){
31962             this.cancelEdit();
31963             return;
31964         } 
31965         this.fireEvent('specialkey', field, e);
31966     
31967     },
31968
31969     /**
31970      * Starts the editing process and shows the editor.
31971      * @param {String/HTMLElement/Element} el The element to edit
31972      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31973       * to the innerHTML of el.
31974      */
31975     startEdit : function(el, value){
31976         if(this.editing){
31977             this.completeEdit();
31978         }
31979         this.boundEl = Roo.get(el);
31980         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31981         if(!this.rendered){
31982             this.render(this.parentEl || document.body);
31983         }
31984         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31985             return;
31986         }
31987         this.startValue = v;
31988         this.field.setValue(v);
31989         if(this.autoSize){
31990             var sz = this.boundEl.getSize();
31991             switch(this.autoSize){
31992                 case "width":
31993                 this.setSize(sz.width,  "");
31994                 break;
31995                 case "height":
31996                 this.setSize("",  sz.height);
31997                 break;
31998                 default:
31999                 this.setSize(sz.width,  sz.height);
32000             }
32001         }
32002         this.el.alignTo(this.boundEl, this.alignment);
32003         this.editing = true;
32004         if(Roo.QuickTips){
32005             Roo.QuickTips.disable();
32006         }
32007         this.show();
32008     },
32009
32010     /**
32011      * Sets the height and width of this editor.
32012      * @param {Number} width The new width
32013      * @param {Number} height The new height
32014      */
32015     setSize : function(w, h){
32016         this.field.setSize(w, h);
32017         if(this.el){
32018             this.el.sync();
32019         }
32020     },
32021
32022     /**
32023      * Realigns the editor to the bound field based on the current alignment config value.
32024      */
32025     realign : function(){
32026         this.el.alignTo(this.boundEl, this.alignment);
32027     },
32028
32029     /**
32030      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32031      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32032      */
32033     completeEdit : function(remainVisible){
32034         if(!this.editing){
32035             return;
32036         }
32037         var v = this.getValue();
32038         if(this.revertInvalid !== false && !this.field.isValid()){
32039             v = this.startValue;
32040             this.cancelEdit(true);
32041         }
32042         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32043             this.editing = false;
32044             this.hide();
32045             return;
32046         }
32047         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32048             this.editing = false;
32049             if(this.updateEl && this.boundEl){
32050                 this.boundEl.update(v);
32051             }
32052             if(remainVisible !== true){
32053                 this.hide();
32054             }
32055             this.fireEvent("complete", this, v, this.startValue);
32056         }
32057     },
32058
32059     // private
32060     onShow : function(){
32061         this.el.show();
32062         if(this.hideEl !== false){
32063             this.boundEl.hide();
32064         }
32065         this.field.show();
32066         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32067             this.fixIEFocus = true;
32068             this.deferredFocus.defer(50, this);
32069         }else{
32070             this.field.focus();
32071         }
32072         this.fireEvent("startedit", this.boundEl, this.startValue);
32073     },
32074
32075     deferredFocus : function(){
32076         if(this.editing){
32077             this.field.focus();
32078         }
32079     },
32080
32081     /**
32082      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32083      * reverted to the original starting value.
32084      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32085      * cancel (defaults to false)
32086      */
32087     cancelEdit : function(remainVisible){
32088         if(this.editing){
32089             this.setValue(this.startValue);
32090             if(remainVisible !== true){
32091                 this.hide();
32092             }
32093         }
32094     },
32095
32096     // private
32097     onBlur : function(){
32098         if(this.allowBlur !== true && this.editing){
32099             this.completeEdit();
32100         }
32101     },
32102
32103     // private
32104     onHide : function(){
32105         if(this.editing){
32106             this.completeEdit();
32107             return;
32108         }
32109         this.field.blur();
32110         if(this.field.collapse){
32111             this.field.collapse();
32112         }
32113         this.el.hide();
32114         if(this.hideEl !== false){
32115             this.boundEl.show();
32116         }
32117         if(Roo.QuickTips){
32118             Roo.QuickTips.enable();
32119         }
32120     },
32121
32122     /**
32123      * Sets the data value of the editor
32124      * @param {Mixed} value Any valid value supported by the underlying field
32125      */
32126     setValue : function(v){
32127         this.field.setValue(v);
32128     },
32129
32130     /**
32131      * Gets the data value of the editor
32132      * @return {Mixed} The data value
32133      */
32134     getValue : function(){
32135         return this.field.getValue();
32136     }
32137 });/*
32138  * Based on:
32139  * Ext JS Library 1.1.1
32140  * Copyright(c) 2006-2007, Ext JS, LLC.
32141  *
32142  * Originally Released Under LGPL - original licence link has changed is not relivant.
32143  *
32144  * Fork - LGPL
32145  * <script type="text/javascript">
32146  */
32147  
32148 /**
32149  * @class Roo.BasicDialog
32150  * @extends Roo.util.Observable
32151  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32152  * <pre><code>
32153 var dlg = new Roo.BasicDialog("my-dlg", {
32154     height: 200,
32155     width: 300,
32156     minHeight: 100,
32157     minWidth: 150,
32158     modal: true,
32159     proxyDrag: true,
32160     shadow: true
32161 });
32162 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32163 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32164 dlg.addButton('Cancel', dlg.hide, dlg);
32165 dlg.show();
32166 </code></pre>
32167   <b>A Dialog should always be a direct child of the body element.</b>
32168  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32169  * @cfg {String} title Default text to display in the title bar (defaults to null)
32170  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32171  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32172  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32173  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32174  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32175  * (defaults to null with no animation)
32176  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32177  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32178  * property for valid values (defaults to 'all')
32179  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32180  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32181  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32182  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32183  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32184  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32185  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32186  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32187  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32188  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32189  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32190  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32191  * draggable = true (defaults to false)
32192  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32193  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32194  * shadow (defaults to false)
32195  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32196  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32197  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32198  * @cfg {Array} buttons Array of buttons
32199  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32200  * @constructor
32201  * Create a new BasicDialog.
32202  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32203  * @param {Object} config Configuration options
32204  */
32205 Roo.BasicDialog = function(el, config){
32206     this.el = Roo.get(el);
32207     var dh = Roo.DomHelper;
32208     if(!this.el && config && config.autoCreate){
32209         if(typeof config.autoCreate == "object"){
32210             if(!config.autoCreate.id){
32211                 config.autoCreate.id = el;
32212             }
32213             this.el = dh.append(document.body,
32214                         config.autoCreate, true);
32215         }else{
32216             this.el = dh.append(document.body,
32217                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32218         }
32219     }
32220     el = this.el;
32221     el.setDisplayed(true);
32222     el.hide = this.hideAction;
32223     this.id = el.id;
32224     el.addClass("x-dlg");
32225
32226     Roo.apply(this, config);
32227
32228     this.proxy = el.createProxy("x-dlg-proxy");
32229     this.proxy.hide = this.hideAction;
32230     this.proxy.setOpacity(.5);
32231     this.proxy.hide();
32232
32233     if(config.width){
32234         el.setWidth(config.width);
32235     }
32236     if(config.height){
32237         el.setHeight(config.height);
32238     }
32239     this.size = el.getSize();
32240     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32241         this.xy = [config.x,config.y];
32242     }else{
32243         this.xy = el.getCenterXY(true);
32244     }
32245     /** The header element @type Roo.Element */
32246     this.header = el.child("> .x-dlg-hd");
32247     /** The body element @type Roo.Element */
32248     this.body = el.child("> .x-dlg-bd");
32249     /** The footer element @type Roo.Element */
32250     this.footer = el.child("> .x-dlg-ft");
32251
32252     if(!this.header){
32253         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32254     }
32255     if(!this.body){
32256         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32257     }
32258
32259     this.header.unselectable();
32260     if(this.title){
32261         this.header.update(this.title);
32262     }
32263     // this element allows the dialog to be focused for keyboard event
32264     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32265     this.focusEl.swallowEvent("click", true);
32266
32267     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32268
32269     // wrap the body and footer for special rendering
32270     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32271     if(this.footer){
32272         this.bwrap.dom.appendChild(this.footer.dom);
32273     }
32274
32275     this.bg = this.el.createChild({
32276         tag: "div", cls:"x-dlg-bg",
32277         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32278     });
32279     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32280
32281
32282     if(this.autoScroll !== false && !this.autoTabs){
32283         this.body.setStyle("overflow", "auto");
32284     }
32285
32286     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32287
32288     if(this.closable !== false){
32289         this.el.addClass("x-dlg-closable");
32290         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32291         this.close.on("click", this.closeClick, this);
32292         this.close.addClassOnOver("x-dlg-close-over");
32293     }
32294     if(this.collapsible !== false){
32295         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32296         this.collapseBtn.on("click", this.collapseClick, this);
32297         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32298         this.header.on("dblclick", this.collapseClick, this);
32299     }
32300     if(this.resizable !== false){
32301         this.el.addClass("x-dlg-resizable");
32302         this.resizer = new Roo.Resizable(el, {
32303             minWidth: this.minWidth || 80,
32304             minHeight:this.minHeight || 80,
32305             handles: this.resizeHandles || "all",
32306             pinned: true
32307         });
32308         this.resizer.on("beforeresize", this.beforeResize, this);
32309         this.resizer.on("resize", this.onResize, this);
32310     }
32311     if(this.draggable !== false){
32312         el.addClass("x-dlg-draggable");
32313         if (!this.proxyDrag) {
32314             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32315         }
32316         else {
32317             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32318         }
32319         dd.setHandleElId(this.header.id);
32320         dd.endDrag = this.endMove.createDelegate(this);
32321         dd.startDrag = this.startMove.createDelegate(this);
32322         dd.onDrag = this.onDrag.createDelegate(this);
32323         dd.scroll = false;
32324         this.dd = dd;
32325     }
32326     if(this.modal){
32327         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32328         this.mask.enableDisplayMode("block");
32329         this.mask.hide();
32330         this.el.addClass("x-dlg-modal");
32331     }
32332     if(this.shadow){
32333         this.shadow = new Roo.Shadow({
32334             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32335             offset : this.shadowOffset
32336         });
32337     }else{
32338         this.shadowOffset = 0;
32339     }
32340     if(Roo.useShims && this.shim !== false){
32341         this.shim = this.el.createShim();
32342         this.shim.hide = this.hideAction;
32343         this.shim.hide();
32344     }else{
32345         this.shim = false;
32346     }
32347     if(this.autoTabs){
32348         this.initTabs();
32349     }
32350     if (this.buttons) { 
32351         var bts= this.buttons;
32352         this.buttons = [];
32353         Roo.each(bts, function(b) {
32354             this.addButton(b);
32355         }, this);
32356     }
32357     
32358     
32359     this.addEvents({
32360         /**
32361          * @event keydown
32362          * Fires when a key is pressed
32363          * @param {Roo.BasicDialog} this
32364          * @param {Roo.EventObject} e
32365          */
32366         "keydown" : true,
32367         /**
32368          * @event move
32369          * Fires when this dialog is moved by the user.
32370          * @param {Roo.BasicDialog} this
32371          * @param {Number} x The new page X
32372          * @param {Number} y The new page Y
32373          */
32374         "move" : true,
32375         /**
32376          * @event resize
32377          * Fires when this dialog is resized by the user.
32378          * @param {Roo.BasicDialog} this
32379          * @param {Number} width The new width
32380          * @param {Number} height The new height
32381          */
32382         "resize" : true,
32383         /**
32384          * @event beforehide
32385          * Fires before this dialog is hidden.
32386          * @param {Roo.BasicDialog} this
32387          */
32388         "beforehide" : true,
32389         /**
32390          * @event hide
32391          * Fires when this dialog is hidden.
32392          * @param {Roo.BasicDialog} this
32393          */
32394         "hide" : true,
32395         /**
32396          * @event beforeshow
32397          * Fires before this dialog is shown.
32398          * @param {Roo.BasicDialog} this
32399          */
32400         "beforeshow" : true,
32401         /**
32402          * @event show
32403          * Fires when this dialog is shown.
32404          * @param {Roo.BasicDialog} this
32405          */
32406         "show" : true
32407     });
32408     el.on("keydown", this.onKeyDown, this);
32409     el.on("mousedown", this.toFront, this);
32410     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32411     this.el.hide();
32412     Roo.DialogManager.register(this);
32413     Roo.BasicDialog.superclass.constructor.call(this);
32414 };
32415
32416 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32417     shadowOffset: Roo.isIE ? 6 : 5,
32418     minHeight: 80,
32419     minWidth: 200,
32420     minButtonWidth: 75,
32421     defaultButton: null,
32422     buttonAlign: "right",
32423     tabTag: 'div',
32424     firstShow: true,
32425
32426     /**
32427      * Sets the dialog title text
32428      * @param {String} text The title text to display
32429      * @return {Roo.BasicDialog} this
32430      */
32431     setTitle : function(text){
32432         this.header.update(text);
32433         return this;
32434     },
32435
32436     // private
32437     closeClick : function(){
32438         this.hide();
32439     },
32440
32441     // private
32442     collapseClick : function(){
32443         this[this.collapsed ? "expand" : "collapse"]();
32444     },
32445
32446     /**
32447      * Collapses the dialog to its minimized state (only the title bar is visible).
32448      * Equivalent to the user clicking the collapse dialog button.
32449      */
32450     collapse : function(){
32451         if(!this.collapsed){
32452             this.collapsed = true;
32453             this.el.addClass("x-dlg-collapsed");
32454             this.restoreHeight = this.el.getHeight();
32455             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32456         }
32457     },
32458
32459     /**
32460      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32461      * clicking the expand dialog button.
32462      */
32463     expand : function(){
32464         if(this.collapsed){
32465             this.collapsed = false;
32466             this.el.removeClass("x-dlg-collapsed");
32467             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32468         }
32469     },
32470
32471     /**
32472      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32473      * @return {Roo.TabPanel} The tabs component
32474      */
32475     initTabs : function(){
32476         var tabs = this.getTabs();
32477         while(tabs.getTab(0)){
32478             tabs.removeTab(0);
32479         }
32480         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32481             var dom = el.dom;
32482             tabs.addTab(Roo.id(dom), dom.title);
32483             dom.title = "";
32484         });
32485         tabs.activate(0);
32486         return tabs;
32487     },
32488
32489     // private
32490     beforeResize : function(){
32491         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32492     },
32493
32494     // private
32495     onResize : function(){
32496         this.refreshSize();
32497         this.syncBodyHeight();
32498         this.adjustAssets();
32499         this.focus();
32500         this.fireEvent("resize", this, this.size.width, this.size.height);
32501     },
32502
32503     // private
32504     onKeyDown : function(e){
32505         if(this.isVisible()){
32506             this.fireEvent("keydown", this, e);
32507         }
32508     },
32509
32510     /**
32511      * Resizes the dialog.
32512      * @param {Number} width
32513      * @param {Number} height
32514      * @return {Roo.BasicDialog} this
32515      */
32516     resizeTo : function(width, height){
32517         this.el.setSize(width, height);
32518         this.size = {width: width, height: height};
32519         this.syncBodyHeight();
32520         if(this.fixedcenter){
32521             this.center();
32522         }
32523         if(this.isVisible()){
32524             this.constrainXY();
32525             this.adjustAssets();
32526         }
32527         this.fireEvent("resize", this, width, height);
32528         return this;
32529     },
32530
32531
32532     /**
32533      * Resizes the dialog to fit the specified content size.
32534      * @param {Number} width
32535      * @param {Number} height
32536      * @return {Roo.BasicDialog} this
32537      */
32538     setContentSize : function(w, h){
32539         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32540         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32541         //if(!this.el.isBorderBox()){
32542             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32543             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32544         //}
32545         if(this.tabs){
32546             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32547             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32548         }
32549         this.resizeTo(w, h);
32550         return this;
32551     },
32552
32553     /**
32554      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32555      * executed in response to a particular key being pressed while the dialog is active.
32556      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32557      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32558      * @param {Function} fn The function to call
32559      * @param {Object} scope (optional) The scope of the function
32560      * @return {Roo.BasicDialog} this
32561      */
32562     addKeyListener : function(key, fn, scope){
32563         var keyCode, shift, ctrl, alt;
32564         if(typeof key == "object" && !(key instanceof Array)){
32565             keyCode = key["key"];
32566             shift = key["shift"];
32567             ctrl = key["ctrl"];
32568             alt = key["alt"];
32569         }else{
32570             keyCode = key;
32571         }
32572         var handler = function(dlg, e){
32573             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32574                 var k = e.getKey();
32575                 if(keyCode instanceof Array){
32576                     for(var i = 0, len = keyCode.length; i < len; i++){
32577                         if(keyCode[i] == k){
32578                           fn.call(scope || window, dlg, k, e);
32579                           return;
32580                         }
32581                     }
32582                 }else{
32583                     if(k == keyCode){
32584                         fn.call(scope || window, dlg, k, e);
32585                     }
32586                 }
32587             }
32588         };
32589         this.on("keydown", handler);
32590         return this;
32591     },
32592
32593     /**
32594      * Returns the TabPanel component (creates it if it doesn't exist).
32595      * Note: If you wish to simply check for the existence of tabs without creating them,
32596      * check for a null 'tabs' property.
32597      * @return {Roo.TabPanel} The tabs component
32598      */
32599     getTabs : function(){
32600         if(!this.tabs){
32601             this.el.addClass("x-dlg-auto-tabs");
32602             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32603             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32604         }
32605         return this.tabs;
32606     },
32607
32608     /**
32609      * Adds a button to the footer section of the dialog.
32610      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32611      * object or a valid Roo.DomHelper element config
32612      * @param {Function} handler The function called when the button is clicked
32613      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32614      * @return {Roo.Button} The new button
32615      */
32616     addButton : function(config, handler, scope){
32617         var dh = Roo.DomHelper;
32618         if(!this.footer){
32619             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32620         }
32621         if(!this.btnContainer){
32622             var tb = this.footer.createChild({
32623
32624                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32625                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32626             }, null, true);
32627             this.btnContainer = tb.firstChild.firstChild.firstChild;
32628         }
32629         var bconfig = {
32630             handler: handler,
32631             scope: scope,
32632             minWidth: this.minButtonWidth,
32633             hideParent:true
32634         };
32635         if(typeof config == "string"){
32636             bconfig.text = config;
32637         }else{
32638             if(config.tag){
32639                 bconfig.dhconfig = config;
32640             }else{
32641                 Roo.apply(bconfig, config);
32642             }
32643         }
32644         var fc = false;
32645         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32646             bconfig.position = Math.max(0, bconfig.position);
32647             fc = this.btnContainer.childNodes[bconfig.position];
32648         }
32649          
32650         var btn = new Roo.Button(
32651             fc ? 
32652                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32653                 : this.btnContainer.appendChild(document.createElement("td")),
32654             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32655             bconfig
32656         );
32657         this.syncBodyHeight();
32658         if(!this.buttons){
32659             /**
32660              * Array of all the buttons that have been added to this dialog via addButton
32661              * @type Array
32662              */
32663             this.buttons = [];
32664         }
32665         this.buttons.push(btn);
32666         return btn;
32667     },
32668
32669     /**
32670      * Sets the default button to be focused when the dialog is displayed.
32671      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32672      * @return {Roo.BasicDialog} this
32673      */
32674     setDefaultButton : function(btn){
32675         this.defaultButton = btn;
32676         return this;
32677     },
32678
32679     // private
32680     getHeaderFooterHeight : function(safe){
32681         var height = 0;
32682         if(this.header){
32683            height += this.header.getHeight();
32684         }
32685         if(this.footer){
32686            var fm = this.footer.getMargins();
32687             height += (this.footer.getHeight()+fm.top+fm.bottom);
32688         }
32689         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32690         height += this.centerBg.getPadding("tb");
32691         return height;
32692     },
32693
32694     // private
32695     syncBodyHeight : function()
32696     {
32697         var bd = this.body, // the text
32698             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32699             bw = this.bwrap;
32700         var height = this.size.height - this.getHeaderFooterHeight(false);
32701         bd.setHeight(height-bd.getMargins("tb"));
32702         var hh = this.header.getHeight();
32703         var h = this.size.height-hh;
32704         cb.setHeight(h);
32705         
32706         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32707         bw.setHeight(h-cb.getPadding("tb"));
32708         
32709         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32710         bd.setWidth(bw.getWidth(true));
32711         if(this.tabs){
32712             this.tabs.syncHeight();
32713             if(Roo.isIE){
32714                 this.tabs.el.repaint();
32715             }
32716         }
32717     },
32718
32719     /**
32720      * Restores the previous state of the dialog if Roo.state is configured.
32721      * @return {Roo.BasicDialog} this
32722      */
32723     restoreState : function(){
32724         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32725         if(box && box.width){
32726             this.xy = [box.x, box.y];
32727             this.resizeTo(box.width, box.height);
32728         }
32729         return this;
32730     },
32731
32732     // private
32733     beforeShow : function(){
32734         this.expand();
32735         if(this.fixedcenter){
32736             this.xy = this.el.getCenterXY(true);
32737         }
32738         if(this.modal){
32739             Roo.get(document.body).addClass("x-body-masked");
32740             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32741             this.mask.show();
32742         }
32743         this.constrainXY();
32744     },
32745
32746     // private
32747     animShow : function(){
32748         var b = Roo.get(this.animateTarget).getBox();
32749         this.proxy.setSize(b.width, b.height);
32750         this.proxy.setLocation(b.x, b.y);
32751         this.proxy.show();
32752         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32753                     true, .35, this.showEl.createDelegate(this));
32754     },
32755
32756     /**
32757      * Shows the dialog.
32758      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32759      * @return {Roo.BasicDialog} this
32760      */
32761     show : function(animateTarget){
32762         if (this.fireEvent("beforeshow", this) === false){
32763             return;
32764         }
32765         if(this.syncHeightBeforeShow){
32766             this.syncBodyHeight();
32767         }else if(this.firstShow){
32768             this.firstShow = false;
32769             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32770         }
32771         this.animateTarget = animateTarget || this.animateTarget;
32772         if(!this.el.isVisible()){
32773             this.beforeShow();
32774             if(this.animateTarget && Roo.get(this.animateTarget)){
32775                 this.animShow();
32776             }else{
32777                 this.showEl();
32778             }
32779         }
32780         return this;
32781     },
32782
32783     // private
32784     showEl : function(){
32785         this.proxy.hide();
32786         this.el.setXY(this.xy);
32787         this.el.show();
32788         this.adjustAssets(true);
32789         this.toFront();
32790         this.focus();
32791         // IE peekaboo bug - fix found by Dave Fenwick
32792         if(Roo.isIE){
32793             this.el.repaint();
32794         }
32795         this.fireEvent("show", this);
32796     },
32797
32798     /**
32799      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32800      * dialog itself will receive focus.
32801      */
32802     focus : function(){
32803         if(this.defaultButton){
32804             this.defaultButton.focus();
32805         }else{
32806             this.focusEl.focus();
32807         }
32808     },
32809
32810     // private
32811     constrainXY : function(){
32812         if(this.constraintoviewport !== false){
32813             if(!this.viewSize){
32814                 if(this.container){
32815                     var s = this.container.getSize();
32816                     this.viewSize = [s.width, s.height];
32817                 }else{
32818                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32819                 }
32820             }
32821             var s = Roo.get(this.container||document).getScroll();
32822
32823             var x = this.xy[0], y = this.xy[1];
32824             var w = this.size.width, h = this.size.height;
32825             var vw = this.viewSize[0], vh = this.viewSize[1];
32826             // only move it if it needs it
32827             var moved = false;
32828             // first validate right/bottom
32829             if(x + w > vw+s.left){
32830                 x = vw - w;
32831                 moved = true;
32832             }
32833             if(y + h > vh+s.top){
32834                 y = vh - h;
32835                 moved = true;
32836             }
32837             // then make sure top/left isn't negative
32838             if(x < s.left){
32839                 x = s.left;
32840                 moved = true;
32841             }
32842             if(y < s.top){
32843                 y = s.top;
32844                 moved = true;
32845             }
32846             if(moved){
32847                 // cache xy
32848                 this.xy = [x, y];
32849                 if(this.isVisible()){
32850                     this.el.setLocation(x, y);
32851                     this.adjustAssets();
32852                 }
32853             }
32854         }
32855     },
32856
32857     // private
32858     onDrag : function(){
32859         if(!this.proxyDrag){
32860             this.xy = this.el.getXY();
32861             this.adjustAssets();
32862         }
32863     },
32864
32865     // private
32866     adjustAssets : function(doShow){
32867         var x = this.xy[0], y = this.xy[1];
32868         var w = this.size.width, h = this.size.height;
32869         if(doShow === true){
32870             if(this.shadow){
32871                 this.shadow.show(this.el);
32872             }
32873             if(this.shim){
32874                 this.shim.show();
32875             }
32876         }
32877         if(this.shadow && this.shadow.isVisible()){
32878             this.shadow.show(this.el);
32879         }
32880         if(this.shim && this.shim.isVisible()){
32881             this.shim.setBounds(x, y, w, h);
32882         }
32883     },
32884
32885     // private
32886     adjustViewport : function(w, h){
32887         if(!w || !h){
32888             w = Roo.lib.Dom.getViewWidth();
32889             h = Roo.lib.Dom.getViewHeight();
32890         }
32891         // cache the size
32892         this.viewSize = [w, h];
32893         if(this.modal && this.mask.isVisible()){
32894             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32895             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32896         }
32897         if(this.isVisible()){
32898             this.constrainXY();
32899         }
32900     },
32901
32902     /**
32903      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32904      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32905      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32906      */
32907     destroy : function(removeEl){
32908         if(this.isVisible()){
32909             this.animateTarget = null;
32910             this.hide();
32911         }
32912         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32913         if(this.tabs){
32914             this.tabs.destroy(removeEl);
32915         }
32916         Roo.destroy(
32917              this.shim,
32918              this.proxy,
32919              this.resizer,
32920              this.close,
32921              this.mask
32922         );
32923         if(this.dd){
32924             this.dd.unreg();
32925         }
32926         if(this.buttons){
32927            for(var i = 0, len = this.buttons.length; i < len; i++){
32928                this.buttons[i].destroy();
32929            }
32930         }
32931         this.el.removeAllListeners();
32932         if(removeEl === true){
32933             this.el.update("");
32934             this.el.remove();
32935         }
32936         Roo.DialogManager.unregister(this);
32937     },
32938
32939     // private
32940     startMove : function(){
32941         if(this.proxyDrag){
32942             this.proxy.show();
32943         }
32944         if(this.constraintoviewport !== false){
32945             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32946         }
32947     },
32948
32949     // private
32950     endMove : function(){
32951         if(!this.proxyDrag){
32952             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32953         }else{
32954             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32955             this.proxy.hide();
32956         }
32957         this.refreshSize();
32958         this.adjustAssets();
32959         this.focus();
32960         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32961     },
32962
32963     /**
32964      * Brings this dialog to the front of any other visible dialogs
32965      * @return {Roo.BasicDialog} this
32966      */
32967     toFront : function(){
32968         Roo.DialogManager.bringToFront(this);
32969         return this;
32970     },
32971
32972     /**
32973      * Sends this dialog to the back (under) of any other visible dialogs
32974      * @return {Roo.BasicDialog} this
32975      */
32976     toBack : function(){
32977         Roo.DialogManager.sendToBack(this);
32978         return this;
32979     },
32980
32981     /**
32982      * Centers this dialog in the viewport
32983      * @return {Roo.BasicDialog} this
32984      */
32985     center : function(){
32986         var xy = this.el.getCenterXY(true);
32987         this.moveTo(xy[0], xy[1]);
32988         return this;
32989     },
32990
32991     /**
32992      * Moves the dialog's top-left corner to the specified point
32993      * @param {Number} x
32994      * @param {Number} y
32995      * @return {Roo.BasicDialog} this
32996      */
32997     moveTo : function(x, y){
32998         this.xy = [x,y];
32999         if(this.isVisible()){
33000             this.el.setXY(this.xy);
33001             this.adjustAssets();
33002         }
33003         return this;
33004     },
33005
33006     /**
33007      * Aligns the dialog to the specified element
33008      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33009      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33010      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33011      * @return {Roo.BasicDialog} this
33012      */
33013     alignTo : function(element, position, offsets){
33014         this.xy = this.el.getAlignToXY(element, position, offsets);
33015         if(this.isVisible()){
33016             this.el.setXY(this.xy);
33017             this.adjustAssets();
33018         }
33019         return this;
33020     },
33021
33022     /**
33023      * Anchors an element to another element and realigns it when the window is resized.
33024      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33025      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33026      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33027      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33028      * is a number, it is used as the buffer delay (defaults to 50ms).
33029      * @return {Roo.BasicDialog} this
33030      */
33031     anchorTo : function(el, alignment, offsets, monitorScroll){
33032         var action = function(){
33033             this.alignTo(el, alignment, offsets);
33034         };
33035         Roo.EventManager.onWindowResize(action, this);
33036         var tm = typeof monitorScroll;
33037         if(tm != 'undefined'){
33038             Roo.EventManager.on(window, 'scroll', action, this,
33039                 {buffer: tm == 'number' ? monitorScroll : 50});
33040         }
33041         action.call(this);
33042         return this;
33043     },
33044
33045     /**
33046      * Returns true if the dialog is visible
33047      * @return {Boolean}
33048      */
33049     isVisible : function(){
33050         return this.el.isVisible();
33051     },
33052
33053     // private
33054     animHide : function(callback){
33055         var b = Roo.get(this.animateTarget).getBox();
33056         this.proxy.show();
33057         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33058         this.el.hide();
33059         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33060                     this.hideEl.createDelegate(this, [callback]));
33061     },
33062
33063     /**
33064      * Hides the dialog.
33065      * @param {Function} callback (optional) Function to call when the dialog is hidden
33066      * @return {Roo.BasicDialog} this
33067      */
33068     hide : function(callback){
33069         if (this.fireEvent("beforehide", this) === false){
33070             return;
33071         }
33072         if(this.shadow){
33073             this.shadow.hide();
33074         }
33075         if(this.shim) {
33076           this.shim.hide();
33077         }
33078         // sometimes animateTarget seems to get set.. causing problems...
33079         // this just double checks..
33080         if(this.animateTarget && Roo.get(this.animateTarget)) {
33081            this.animHide(callback);
33082         }else{
33083             this.el.hide();
33084             this.hideEl(callback);
33085         }
33086         return this;
33087     },
33088
33089     // private
33090     hideEl : function(callback){
33091         this.proxy.hide();
33092         if(this.modal){
33093             this.mask.hide();
33094             Roo.get(document.body).removeClass("x-body-masked");
33095         }
33096         this.fireEvent("hide", this);
33097         if(typeof callback == "function"){
33098             callback();
33099         }
33100     },
33101
33102     // private
33103     hideAction : function(){
33104         this.setLeft("-10000px");
33105         this.setTop("-10000px");
33106         this.setStyle("visibility", "hidden");
33107     },
33108
33109     // private
33110     refreshSize : function(){
33111         this.size = this.el.getSize();
33112         this.xy = this.el.getXY();
33113         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33114     },
33115
33116     // private
33117     // z-index is managed by the DialogManager and may be overwritten at any time
33118     setZIndex : function(index){
33119         if(this.modal){
33120             this.mask.setStyle("z-index", index);
33121         }
33122         if(this.shim){
33123             this.shim.setStyle("z-index", ++index);
33124         }
33125         if(this.shadow){
33126             this.shadow.setZIndex(++index);
33127         }
33128         this.el.setStyle("z-index", ++index);
33129         if(this.proxy){
33130             this.proxy.setStyle("z-index", ++index);
33131         }
33132         if(this.resizer){
33133             this.resizer.proxy.setStyle("z-index", ++index);
33134         }
33135
33136         this.lastZIndex = index;
33137     },
33138
33139     /**
33140      * Returns the element for this dialog
33141      * @return {Roo.Element} The underlying dialog Element
33142      */
33143     getEl : function(){
33144         return this.el;
33145     }
33146 });
33147
33148 /**
33149  * @class Roo.DialogManager
33150  * Provides global access to BasicDialogs that have been created and
33151  * support for z-indexing (layering) multiple open dialogs.
33152  */
33153 Roo.DialogManager = function(){
33154     var list = {};
33155     var accessList = [];
33156     var front = null;
33157
33158     // private
33159     var sortDialogs = function(d1, d2){
33160         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33161     };
33162
33163     // private
33164     var orderDialogs = function(){
33165         accessList.sort(sortDialogs);
33166         var seed = Roo.DialogManager.zseed;
33167         for(var i = 0, len = accessList.length; i < len; i++){
33168             var dlg = accessList[i];
33169             if(dlg){
33170                 dlg.setZIndex(seed + (i*10));
33171             }
33172         }
33173     };
33174
33175     return {
33176         /**
33177          * The starting z-index for BasicDialogs (defaults to 9000)
33178          * @type Number The z-index value
33179          */
33180         zseed : 9000,
33181
33182         // private
33183         register : function(dlg){
33184             list[dlg.id] = dlg;
33185             accessList.push(dlg);
33186         },
33187
33188         // private
33189         unregister : function(dlg){
33190             delete list[dlg.id];
33191             var i=0;
33192             var len=0;
33193             if(!accessList.indexOf){
33194                 for(  i = 0, len = accessList.length; i < len; i++){
33195                     if(accessList[i] == dlg){
33196                         accessList.splice(i, 1);
33197                         return;
33198                     }
33199                 }
33200             }else{
33201                  i = accessList.indexOf(dlg);
33202                 if(i != -1){
33203                     accessList.splice(i, 1);
33204                 }
33205             }
33206         },
33207
33208         /**
33209          * Gets a registered dialog by id
33210          * @param {String/Object} id The id of the dialog or a dialog
33211          * @return {Roo.BasicDialog} this
33212          */
33213         get : function(id){
33214             return typeof id == "object" ? id : list[id];
33215         },
33216
33217         /**
33218          * Brings the specified dialog to the front
33219          * @param {String/Object} dlg The id of the dialog or a dialog
33220          * @return {Roo.BasicDialog} this
33221          */
33222         bringToFront : function(dlg){
33223             dlg = this.get(dlg);
33224             if(dlg != front){
33225                 front = dlg;
33226                 dlg._lastAccess = new Date().getTime();
33227                 orderDialogs();
33228             }
33229             return dlg;
33230         },
33231
33232         /**
33233          * Sends the specified dialog to the back
33234          * @param {String/Object} dlg The id of the dialog or a dialog
33235          * @return {Roo.BasicDialog} this
33236          */
33237         sendToBack : function(dlg){
33238             dlg = this.get(dlg);
33239             dlg._lastAccess = -(new Date().getTime());
33240             orderDialogs();
33241             return dlg;
33242         },
33243
33244         /**
33245          * Hides all dialogs
33246          */
33247         hideAll : function(){
33248             for(var id in list){
33249                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33250                     list[id].hide();
33251                 }
33252             }
33253         }
33254     };
33255 }();
33256
33257 /**
33258  * @class Roo.LayoutDialog
33259  * @extends Roo.BasicDialog
33260  * @children Roo.ContentPanel
33261  * Dialog which provides adjustments for working with a layout in a Dialog.
33262  * Add your necessary layout config options to the dialog's config.<br>
33263  * Example usage (including a nested layout):
33264  * <pre><code>
33265 if(!dialog){
33266     dialog = new Roo.LayoutDialog("download-dlg", {
33267         modal: true,
33268         width:600,
33269         height:450,
33270         shadow:true,
33271         minWidth:500,
33272         minHeight:350,
33273         autoTabs:true,
33274         proxyDrag:true,
33275         // layout config merges with the dialog config
33276         center:{
33277             tabPosition: "top",
33278             alwaysShowTabs: true
33279         }
33280     });
33281     dialog.addKeyListener(27, dialog.hide, dialog);
33282     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33283     dialog.addButton("Build It!", this.getDownload, this);
33284
33285     // we can even add nested layouts
33286     var innerLayout = new Roo.BorderLayout("dl-inner", {
33287         east: {
33288             initialSize: 200,
33289             autoScroll:true,
33290             split:true
33291         },
33292         center: {
33293             autoScroll:true
33294         }
33295     });
33296     innerLayout.beginUpdate();
33297     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33298     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33299     innerLayout.endUpdate(true);
33300
33301     var layout = dialog.getLayout();
33302     layout.beginUpdate();
33303     layout.add("center", new Roo.ContentPanel("standard-panel",
33304                         {title: "Download the Source", fitToFrame:true}));
33305     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33306                {title: "Build your own roo.js"}));
33307     layout.getRegion("center").showPanel(sp);
33308     layout.endUpdate();
33309 }
33310 </code></pre>
33311     * @constructor
33312     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33313     * @param {Object} config configuration options
33314   */
33315 Roo.LayoutDialog = function(el, cfg){
33316     
33317     var config=  cfg;
33318     if (typeof(cfg) == 'undefined') {
33319         config = Roo.apply({}, el);
33320         // not sure why we use documentElement here.. - it should always be body.
33321         // IE7 borks horribly if we use documentElement.
33322         // webkit also does not like documentElement - it creates a body element...
33323         el = Roo.get( document.body || document.documentElement ).createChild();
33324         //config.autoCreate = true;
33325     }
33326     
33327     
33328     config.autoTabs = false;
33329     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33330     this.body.setStyle({overflow:"hidden", position:"relative"});
33331     this.layout = new Roo.BorderLayout(this.body.dom, config);
33332     this.layout.monitorWindowResize = false;
33333     this.el.addClass("x-dlg-auto-layout");
33334     // fix case when center region overwrites center function
33335     this.center = Roo.BasicDialog.prototype.center;
33336     this.on("show", this.layout.layout, this.layout, true);
33337     if (config.items) {
33338         var xitems = config.items;
33339         delete config.items;
33340         Roo.each(xitems, this.addxtype, this);
33341     }
33342     
33343     
33344 };
33345 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33346     
33347     
33348     /**
33349      * @cfg {Roo.LayoutRegion} east  
33350      */
33351     /**
33352      * @cfg {Roo.LayoutRegion} west
33353      */
33354     /**
33355      * @cfg {Roo.LayoutRegion} south
33356      */
33357     /**
33358      * @cfg {Roo.LayoutRegion} north
33359      */
33360     /**
33361      * @cfg {Roo.LayoutRegion} center
33362      */
33363     /**
33364      * @cfg {Roo.Button} buttons[]  Bottom buttons..
33365      */
33366     
33367     
33368     /**
33369      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33370      * @deprecated
33371      */
33372     endUpdate : function(){
33373         this.layout.endUpdate();
33374     },
33375
33376     /**
33377      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33378      *  @deprecated
33379      */
33380     beginUpdate : function(){
33381         this.layout.beginUpdate();
33382     },
33383
33384     /**
33385      * Get the BorderLayout for this dialog
33386      * @return {Roo.BorderLayout}
33387      */
33388     getLayout : function(){
33389         return this.layout;
33390     },
33391
33392     showEl : function(){
33393         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33394         if(Roo.isIE7){
33395             this.layout.layout();
33396         }
33397     },
33398
33399     // private
33400     // Use the syncHeightBeforeShow config option to control this automatically
33401     syncBodyHeight : function(){
33402         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33403         if(this.layout){this.layout.layout();}
33404     },
33405     
33406       /**
33407      * Add an xtype element (actually adds to the layout.)
33408      * @return {Object} xdata xtype object data.
33409      */
33410     
33411     addxtype : function(c) {
33412         return this.layout.addxtype(c);
33413     }
33414 });/*
33415  * Based on:
33416  * Ext JS Library 1.1.1
33417  * Copyright(c) 2006-2007, Ext JS, LLC.
33418  *
33419  * Originally Released Under LGPL - original licence link has changed is not relivant.
33420  *
33421  * Fork - LGPL
33422  * <script type="text/javascript">
33423  */
33424  
33425 /**
33426  * @class Roo.MessageBox
33427  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33428  * Example usage:
33429  *<pre><code>
33430 // Basic alert:
33431 Roo.Msg.alert('Status', 'Changes saved successfully.');
33432
33433 // Prompt for user data:
33434 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33435     if (btn == 'ok'){
33436         // process text value...
33437     }
33438 });
33439
33440 // Show a dialog using config options:
33441 Roo.Msg.show({
33442    title:'Save Changes?',
33443    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33444    buttons: Roo.Msg.YESNOCANCEL,
33445    fn: processResult,
33446    animEl: 'elId'
33447 });
33448 </code></pre>
33449  * @singleton
33450  */
33451 Roo.MessageBox = function(){
33452     var dlg, opt, mask, waitTimer;
33453     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33454     var buttons, activeTextEl, bwidth;
33455
33456     // private
33457     var handleButton = function(button){
33458         dlg.hide();
33459         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33460     };
33461
33462     // private
33463     var handleHide = function(){
33464         if(opt && opt.cls){
33465             dlg.el.removeClass(opt.cls);
33466         }
33467         if(waitTimer){
33468             Roo.TaskMgr.stop(waitTimer);
33469             waitTimer = null;
33470         }
33471     };
33472
33473     // private
33474     var updateButtons = function(b){
33475         var width = 0;
33476         if(!b){
33477             buttons["ok"].hide();
33478             buttons["cancel"].hide();
33479             buttons["yes"].hide();
33480             buttons["no"].hide();
33481             dlg.footer.dom.style.display = 'none';
33482             return width;
33483         }
33484         dlg.footer.dom.style.display = '';
33485         for(var k in buttons){
33486             if(typeof buttons[k] != "function"){
33487                 if(b[k]){
33488                     buttons[k].show();
33489                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33490                     width += buttons[k].el.getWidth()+15;
33491                 }else{
33492                     buttons[k].hide();
33493                 }
33494             }
33495         }
33496         return width;
33497     };
33498
33499     // private
33500     var handleEsc = function(d, k, e){
33501         if(opt && opt.closable !== false){
33502             dlg.hide();
33503         }
33504         if(e){
33505             e.stopEvent();
33506         }
33507     };
33508
33509     return {
33510         /**
33511          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33512          * @return {Roo.BasicDialog} The BasicDialog element
33513          */
33514         getDialog : function(){
33515            if(!dlg){
33516                 dlg = new Roo.BasicDialog("x-msg-box", {
33517                     autoCreate : true,
33518                     shadow: true,
33519                     draggable: true,
33520                     resizable:false,
33521                     constraintoviewport:false,
33522                     fixedcenter:true,
33523                     collapsible : false,
33524                     shim:true,
33525                     modal: true,
33526                     width:400, height:100,
33527                     buttonAlign:"center",
33528                     closeClick : function(){
33529                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33530                             handleButton("no");
33531                         }else{
33532                             handleButton("cancel");
33533                         }
33534                     }
33535                 });
33536                 dlg.on("hide", handleHide);
33537                 mask = dlg.mask;
33538                 dlg.addKeyListener(27, handleEsc);
33539                 buttons = {};
33540                 var bt = this.buttonText;
33541                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33542                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33543                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33544                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33545                 bodyEl = dlg.body.createChild({
33546
33547                     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>'
33548                 });
33549                 msgEl = bodyEl.dom.firstChild;
33550                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33551                 textboxEl.enableDisplayMode();
33552                 textboxEl.addKeyListener([10,13], function(){
33553                     if(dlg.isVisible() && opt && opt.buttons){
33554                         if(opt.buttons.ok){
33555                             handleButton("ok");
33556                         }else if(opt.buttons.yes){
33557                             handleButton("yes");
33558                         }
33559                     }
33560                 });
33561                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33562                 textareaEl.enableDisplayMode();
33563                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33564                 progressEl.enableDisplayMode();
33565                 var pf = progressEl.dom.firstChild;
33566                 if (pf) {
33567                     pp = Roo.get(pf.firstChild);
33568                     pp.setHeight(pf.offsetHeight);
33569                 }
33570                 
33571             }
33572             return dlg;
33573         },
33574
33575         /**
33576          * Updates the message box body text
33577          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33578          * the XHTML-compliant non-breaking space character '&amp;#160;')
33579          * @return {Roo.MessageBox} This message box
33580          */
33581         updateText : function(text){
33582             if(!dlg.isVisible() && !opt.width){
33583                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33584             }
33585             msgEl.innerHTML = text || '&#160;';
33586       
33587             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33588             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33589             var w = Math.max(
33590                     Math.min(opt.width || cw , this.maxWidth), 
33591                     Math.max(opt.minWidth || this.minWidth, bwidth)
33592             );
33593             if(opt.prompt){
33594                 activeTextEl.setWidth(w);
33595             }
33596             if(dlg.isVisible()){
33597                 dlg.fixedcenter = false;
33598             }
33599             // to big, make it scroll. = But as usual stupid IE does not support
33600             // !important..
33601             
33602             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33603                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33604                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33605             } else {
33606                 bodyEl.dom.style.height = '';
33607                 bodyEl.dom.style.overflowY = '';
33608             }
33609             if (cw > w) {
33610                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33611             } else {
33612                 bodyEl.dom.style.overflowX = '';
33613             }
33614             
33615             dlg.setContentSize(w, bodyEl.getHeight());
33616             if(dlg.isVisible()){
33617                 dlg.fixedcenter = true;
33618             }
33619             return this;
33620         },
33621
33622         /**
33623          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33624          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33625          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33626          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33627          * @return {Roo.MessageBox} This message box
33628          */
33629         updateProgress : function(value, text){
33630             if(text){
33631                 this.updateText(text);
33632             }
33633             if (pp) { // weird bug on my firefox - for some reason this is not defined
33634                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33635             }
33636             return this;
33637         },        
33638
33639         /**
33640          * Returns true if the message box is currently displayed
33641          * @return {Boolean} True if the message box is visible, else false
33642          */
33643         isVisible : function(){
33644             return dlg && dlg.isVisible();  
33645         },
33646
33647         /**
33648          * Hides the message box if it is displayed
33649          */
33650         hide : function(){
33651             if(this.isVisible()){
33652                 dlg.hide();
33653             }  
33654         },
33655
33656         /**
33657          * Displays a new message box, or reinitializes an existing message box, based on the config options
33658          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33659          * The following config object properties are supported:
33660          * <pre>
33661 Property    Type             Description
33662 ----------  ---------------  ------------------------------------------------------------------------------------
33663 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33664                                    closes (defaults to undefined)
33665 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33666                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33667 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33668                                    progress and wait dialogs will ignore this property and always hide the
33669                                    close button as they can only be closed programmatically.
33670 cls               String           A custom CSS class to apply to the message box element
33671 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33672                                    displayed (defaults to 75)
33673 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33674                                    function will be btn (the name of the button that was clicked, if applicable,
33675                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33676                                    Progress and wait dialogs will ignore this option since they do not respond to
33677                                    user actions and can only be closed programmatically, so any required function
33678                                    should be called by the same code after it closes the dialog.
33679 icon              String           A CSS class that provides a background image to be used as an icon for
33680                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33681 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33682 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33683 modal             Boolean          False to allow user interaction with the page while the message box is
33684                                    displayed (defaults to true)
33685 msg               String           A string that will replace the existing message box body text (defaults
33686                                    to the XHTML-compliant non-breaking space character '&#160;')
33687 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33688 progress          Boolean          True to display a progress bar (defaults to false)
33689 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33690 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33691 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33692 title             String           The title text
33693 value             String           The string value to set into the active textbox element if displayed
33694 wait              Boolean          True to display a progress bar (defaults to false)
33695 width             Number           The width of the dialog in pixels
33696 </pre>
33697          *
33698          * Example usage:
33699          * <pre><code>
33700 Roo.Msg.show({
33701    title: 'Address',
33702    msg: 'Please enter your address:',
33703    width: 300,
33704    buttons: Roo.MessageBox.OKCANCEL,
33705    multiline: true,
33706    fn: saveAddress,
33707    animEl: 'addAddressBtn'
33708 });
33709 </code></pre>
33710          * @param {Object} config Configuration options
33711          * @return {Roo.MessageBox} This message box
33712          */
33713         show : function(options)
33714         {
33715             
33716             // this causes nightmares if you show one dialog after another
33717             // especially on callbacks..
33718              
33719             if(this.isVisible()){
33720                 
33721                 this.hide();
33722                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33723                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33724                 Roo.log("New Dialog Message:" +  options.msg )
33725                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33726                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33727                 
33728             }
33729             var d = this.getDialog();
33730             opt = options;
33731             d.setTitle(opt.title || "&#160;");
33732             d.close.setDisplayed(opt.closable !== false);
33733             activeTextEl = textboxEl;
33734             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33735             if(opt.prompt){
33736                 if(opt.multiline){
33737                     textboxEl.hide();
33738                     textareaEl.show();
33739                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33740                         opt.multiline : this.defaultTextHeight);
33741                     activeTextEl = textareaEl;
33742                 }else{
33743                     textboxEl.show();
33744                     textareaEl.hide();
33745                 }
33746             }else{
33747                 textboxEl.hide();
33748                 textareaEl.hide();
33749             }
33750             progressEl.setDisplayed(opt.progress === true);
33751             this.updateProgress(0);
33752             activeTextEl.dom.value = opt.value || "";
33753             if(opt.prompt){
33754                 dlg.setDefaultButton(activeTextEl);
33755             }else{
33756                 var bs = opt.buttons;
33757                 var db = null;
33758                 if(bs && bs.ok){
33759                     db = buttons["ok"];
33760                 }else if(bs && bs.yes){
33761                     db = buttons["yes"];
33762                 }
33763                 dlg.setDefaultButton(db);
33764             }
33765             bwidth = updateButtons(opt.buttons);
33766             this.updateText(opt.msg);
33767             if(opt.cls){
33768                 d.el.addClass(opt.cls);
33769             }
33770             d.proxyDrag = opt.proxyDrag === true;
33771             d.modal = opt.modal !== false;
33772             d.mask = opt.modal !== false ? mask : false;
33773             if(!d.isVisible()){
33774                 // force it to the end of the z-index stack so it gets a cursor in FF
33775                 document.body.appendChild(dlg.el.dom);
33776                 d.animateTarget = null;
33777                 d.show(options.animEl);
33778             }
33779             return this;
33780         },
33781
33782         /**
33783          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33784          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33785          * and closing the message box when the process is complete.
33786          * @param {String} title The title bar text
33787          * @param {String} msg The message box body text
33788          * @return {Roo.MessageBox} This message box
33789          */
33790         progress : function(title, msg){
33791             this.show({
33792                 title : title,
33793                 msg : msg,
33794                 buttons: false,
33795                 progress:true,
33796                 closable:false,
33797                 minWidth: this.minProgressWidth,
33798                 modal : true
33799             });
33800             return this;
33801         },
33802
33803         /**
33804          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33805          * If a callback function is passed it will be called after the user clicks the button, and the
33806          * id of the button that was clicked will be passed as the only parameter to the callback
33807          * (could also be the top-right close button).
33808          * @param {String} title The title bar text
33809          * @param {String} msg The message box body text
33810          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33811          * @param {Object} scope (optional) The scope of the callback function
33812          * @return {Roo.MessageBox} This message box
33813          */
33814         alert : function(title, msg, fn, scope){
33815             this.show({
33816                 title : title,
33817                 msg : msg,
33818                 buttons: this.OK,
33819                 fn: fn,
33820                 scope : scope,
33821                 modal : true
33822             });
33823             return this;
33824         },
33825
33826         /**
33827          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33828          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33829          * You are responsible for closing the message box when the process is complete.
33830          * @param {String} msg The message box body text
33831          * @param {String} title (optional) The title bar text
33832          * @return {Roo.MessageBox} This message box
33833          */
33834         wait : function(msg, title){
33835             this.show({
33836                 title : title,
33837                 msg : msg,
33838                 buttons: false,
33839                 closable:false,
33840                 progress:true,
33841                 modal:true,
33842                 width:300,
33843                 wait:true
33844             });
33845             waitTimer = Roo.TaskMgr.start({
33846                 run: function(i){
33847                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33848                 },
33849                 interval: 1000
33850             });
33851             return this;
33852         },
33853
33854         /**
33855          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33856          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33857          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33858          * @param {String} title The title bar text
33859          * @param {String} msg The message box body text
33860          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33861          * @param {Object} scope (optional) The scope of the callback function
33862          * @return {Roo.MessageBox} This message box
33863          */
33864         confirm : function(title, msg, fn, scope){
33865             this.show({
33866                 title : title,
33867                 msg : msg,
33868                 buttons: this.YESNO,
33869                 fn: fn,
33870                 scope : scope,
33871                 modal : true
33872             });
33873             return this;
33874         },
33875
33876         /**
33877          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33878          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33879          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33880          * (could also be the top-right close button) and the text that was entered will be passed as the two
33881          * parameters to the callback.
33882          * @param {String} title The title bar text
33883          * @param {String} msg The message box body text
33884          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33885          * @param {Object} scope (optional) The scope of the callback function
33886          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33887          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33888          * @return {Roo.MessageBox} This message box
33889          */
33890         prompt : function(title, msg, fn, scope, multiline){
33891             this.show({
33892                 title : title,
33893                 msg : msg,
33894                 buttons: this.OKCANCEL,
33895                 fn: fn,
33896                 minWidth:250,
33897                 scope : scope,
33898                 prompt:true,
33899                 multiline: multiline,
33900                 modal : true
33901             });
33902             return this;
33903         },
33904
33905         /**
33906          * Button config that displays a single OK button
33907          * @type Object
33908          */
33909         OK : {ok:true},
33910         /**
33911          * Button config that displays Yes and No buttons
33912          * @type Object
33913          */
33914         YESNO : {yes:true, no:true},
33915         /**
33916          * Button config that displays OK and Cancel buttons
33917          * @type Object
33918          */
33919         OKCANCEL : {ok:true, cancel:true},
33920         /**
33921          * Button config that displays Yes, No and Cancel buttons
33922          * @type Object
33923          */
33924         YESNOCANCEL : {yes:true, no:true, cancel:true},
33925
33926         /**
33927          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33928          * @type Number
33929          */
33930         defaultTextHeight : 75,
33931         /**
33932          * The maximum width in pixels of the message box (defaults to 600)
33933          * @type Number
33934          */
33935         maxWidth : 600,
33936         /**
33937          * The minimum width in pixels of the message box (defaults to 100)
33938          * @type Number
33939          */
33940         minWidth : 100,
33941         /**
33942          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33943          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33944          * @type Number
33945          */
33946         minProgressWidth : 250,
33947         /**
33948          * An object containing the default button text strings that can be overriden for localized language support.
33949          * Supported properties are: ok, cancel, yes and no.
33950          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33951          * @type Object
33952          */
33953         buttonText : {
33954             ok : "OK",
33955             cancel : "Cancel",
33956             yes : "Yes",
33957             no : "No"
33958         }
33959     };
33960 }();
33961
33962 /**
33963  * Shorthand for {@link Roo.MessageBox}
33964  */
33965 Roo.Msg = Roo.MessageBox;/*
33966  * Based on:
33967  * Ext JS Library 1.1.1
33968  * Copyright(c) 2006-2007, Ext JS, LLC.
33969  *
33970  * Originally Released Under LGPL - original licence link has changed is not relivant.
33971  *
33972  * Fork - LGPL
33973  * <script type="text/javascript">
33974  */
33975 /**
33976  * @class Roo.QuickTips
33977  * Provides attractive and customizable tooltips for any element.
33978  * @singleton
33979  */
33980 Roo.QuickTips = function(){
33981     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33982     var ce, bd, xy, dd;
33983     var visible = false, disabled = true, inited = false;
33984     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33985     
33986     var onOver = function(e){
33987         if(disabled){
33988             return;
33989         }
33990         var t = e.getTarget();
33991         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33992             return;
33993         }
33994         if(ce && t == ce.el){
33995             clearTimeout(hideProc);
33996             return;
33997         }
33998         if(t && tagEls[t.id]){
33999             tagEls[t.id].el = t;
34000             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34001             return;
34002         }
34003         var ttp, et = Roo.fly(t);
34004         var ns = cfg.namespace;
34005         if(tm.interceptTitles && t.title){
34006             ttp = t.title;
34007             t.qtip = ttp;
34008             t.removeAttribute("title");
34009             e.preventDefault();
34010         }else{
34011             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34012         }
34013         if(ttp){
34014             showProc = show.defer(tm.showDelay, tm, [{
34015                 el: t, 
34016                 text: ttp.replace(/\\n/g,'<br/>'),
34017                 width: et.getAttributeNS(ns, cfg.width),
34018                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34019                 title: et.getAttributeNS(ns, cfg.title),
34020                     cls: et.getAttributeNS(ns, cfg.cls)
34021             }]);
34022         }
34023     };
34024     
34025     var onOut = function(e){
34026         clearTimeout(showProc);
34027         var t = e.getTarget();
34028         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34029             hideProc = setTimeout(hide, tm.hideDelay);
34030         }
34031     };
34032     
34033     var onMove = function(e){
34034         if(disabled){
34035             return;
34036         }
34037         xy = e.getXY();
34038         xy[1] += 18;
34039         if(tm.trackMouse && ce){
34040             el.setXY(xy);
34041         }
34042     };
34043     
34044     var onDown = function(e){
34045         clearTimeout(showProc);
34046         clearTimeout(hideProc);
34047         if(!e.within(el)){
34048             if(tm.hideOnClick){
34049                 hide();
34050                 tm.disable();
34051                 tm.enable.defer(100, tm);
34052             }
34053         }
34054     };
34055     
34056     var getPad = function(){
34057         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34058     };
34059
34060     var show = function(o){
34061         if(disabled){
34062             return;
34063         }
34064         clearTimeout(dismissProc);
34065         ce = o;
34066         if(removeCls){ // in case manually hidden
34067             el.removeClass(removeCls);
34068             removeCls = null;
34069         }
34070         if(ce.cls){
34071             el.addClass(ce.cls);
34072             removeCls = ce.cls;
34073         }
34074         if(ce.title){
34075             tipTitle.update(ce.title);
34076             tipTitle.show();
34077         }else{
34078             tipTitle.update('');
34079             tipTitle.hide();
34080         }
34081         el.dom.style.width  = tm.maxWidth+'px';
34082         //tipBody.dom.style.width = '';
34083         tipBodyText.update(o.text);
34084         var p = getPad(), w = ce.width;
34085         if(!w){
34086             var td = tipBodyText.dom;
34087             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34088             if(aw > tm.maxWidth){
34089                 w = tm.maxWidth;
34090             }else if(aw < tm.minWidth){
34091                 w = tm.minWidth;
34092             }else{
34093                 w = aw;
34094             }
34095         }
34096         //tipBody.setWidth(w);
34097         el.setWidth(parseInt(w, 10) + p);
34098         if(ce.autoHide === false){
34099             close.setDisplayed(true);
34100             if(dd){
34101                 dd.unlock();
34102             }
34103         }else{
34104             close.setDisplayed(false);
34105             if(dd){
34106                 dd.lock();
34107             }
34108         }
34109         if(xy){
34110             el.avoidY = xy[1]-18;
34111             el.setXY(xy);
34112         }
34113         if(tm.animate){
34114             el.setOpacity(.1);
34115             el.setStyle("visibility", "visible");
34116             el.fadeIn({callback: afterShow});
34117         }else{
34118             afterShow();
34119         }
34120     };
34121     
34122     var afterShow = function(){
34123         if(ce){
34124             el.show();
34125             esc.enable();
34126             if(tm.autoDismiss && ce.autoHide !== false){
34127                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34128             }
34129         }
34130     };
34131     
34132     var hide = function(noanim){
34133         clearTimeout(dismissProc);
34134         clearTimeout(hideProc);
34135         ce = null;
34136         if(el.isVisible()){
34137             esc.disable();
34138             if(noanim !== true && tm.animate){
34139                 el.fadeOut({callback: afterHide});
34140             }else{
34141                 afterHide();
34142             } 
34143         }
34144     };
34145     
34146     var afterHide = function(){
34147         el.hide();
34148         if(removeCls){
34149             el.removeClass(removeCls);
34150             removeCls = null;
34151         }
34152     };
34153     
34154     return {
34155         /**
34156         * @cfg {Number} minWidth
34157         * The minimum width of the quick tip (defaults to 40)
34158         */
34159        minWidth : 40,
34160         /**
34161         * @cfg {Number} maxWidth
34162         * The maximum width of the quick tip (defaults to 300)
34163         */
34164        maxWidth : 300,
34165         /**
34166         * @cfg {Boolean} interceptTitles
34167         * True to automatically use the element's DOM title value if available (defaults to false)
34168         */
34169        interceptTitles : false,
34170         /**
34171         * @cfg {Boolean} trackMouse
34172         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34173         */
34174        trackMouse : false,
34175         /**
34176         * @cfg {Boolean} hideOnClick
34177         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34178         */
34179        hideOnClick : true,
34180         /**
34181         * @cfg {Number} showDelay
34182         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34183         */
34184        showDelay : 500,
34185         /**
34186         * @cfg {Number} hideDelay
34187         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34188         */
34189        hideDelay : 200,
34190         /**
34191         * @cfg {Boolean} autoHide
34192         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34193         * Used in conjunction with hideDelay.
34194         */
34195        autoHide : true,
34196         /**
34197         * @cfg {Boolean}
34198         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34199         * (defaults to true).  Used in conjunction with autoDismissDelay.
34200         */
34201        autoDismiss : true,
34202         /**
34203         * @cfg {Number}
34204         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34205         */
34206        autoDismissDelay : 5000,
34207        /**
34208         * @cfg {Boolean} animate
34209         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34210         */
34211        animate : false,
34212
34213        /**
34214         * @cfg {String} title
34215         * Title text to display (defaults to '').  This can be any valid HTML markup.
34216         */
34217         title: '',
34218        /**
34219         * @cfg {String} text
34220         * Body text to display (defaults to '').  This can be any valid HTML markup.
34221         */
34222         text : '',
34223        /**
34224         * @cfg {String} cls
34225         * A CSS class to apply to the base quick tip element (defaults to '').
34226         */
34227         cls : '',
34228        /**
34229         * @cfg {Number} width
34230         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34231         * minWidth or maxWidth.
34232         */
34233         width : null,
34234
34235     /**
34236      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34237      * or display QuickTips in a page.
34238      */
34239        init : function(){
34240           tm = Roo.QuickTips;
34241           cfg = tm.tagConfig;
34242           if(!inited){
34243               if(!Roo.isReady){ // allow calling of init() before onReady
34244                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34245                   return;
34246               }
34247               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34248               el.fxDefaults = {stopFx: true};
34249               // maximum custom styling
34250               //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>');
34251               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>');              
34252               tipTitle = el.child('h3');
34253               tipTitle.enableDisplayMode("block");
34254               tipBody = el.child('div.x-tip-bd');
34255               tipBodyText = el.child('div.x-tip-bd-inner');
34256               //bdLeft = el.child('div.x-tip-bd-left');
34257               //bdRight = el.child('div.x-tip-bd-right');
34258               close = el.child('div.x-tip-close');
34259               close.enableDisplayMode("block");
34260               close.on("click", hide);
34261               var d = Roo.get(document);
34262               d.on("mousedown", onDown);
34263               d.on("mouseover", onOver);
34264               d.on("mouseout", onOut);
34265               d.on("mousemove", onMove);
34266               esc = d.addKeyListener(27, hide);
34267               esc.disable();
34268               if(Roo.dd.DD){
34269                   dd = el.initDD("default", null, {
34270                       onDrag : function(){
34271                           el.sync();  
34272                       }
34273                   });
34274                   dd.setHandleElId(tipTitle.id);
34275                   dd.lock();
34276               }
34277               inited = true;
34278           }
34279           this.enable(); 
34280        },
34281
34282     /**
34283      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34284      * are supported:
34285      * <pre>
34286 Property    Type                   Description
34287 ----------  ---------------------  ------------------------------------------------------------------------
34288 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34289      * </ul>
34290      * @param {Object} config The config object
34291      */
34292        register : function(config){
34293            var cs = config instanceof Array ? config : arguments;
34294            for(var i = 0, len = cs.length; i < len; i++) {
34295                var c = cs[i];
34296                var target = c.target;
34297                if(target){
34298                    if(target instanceof Array){
34299                        for(var j = 0, jlen = target.length; j < jlen; j++){
34300                            tagEls[target[j]] = c;
34301                        }
34302                    }else{
34303                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34304                    }
34305                }
34306            }
34307        },
34308
34309     /**
34310      * Removes this quick tip from its element and destroys it.
34311      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34312      */
34313        unregister : function(el){
34314            delete tagEls[Roo.id(el)];
34315        },
34316
34317     /**
34318      * Enable this quick tip.
34319      */
34320        enable : function(){
34321            if(inited && disabled){
34322                locks.pop();
34323                if(locks.length < 1){
34324                    disabled = false;
34325                }
34326            }
34327        },
34328
34329     /**
34330      * Disable this quick tip.
34331      */
34332        disable : function(){
34333           disabled = true;
34334           clearTimeout(showProc);
34335           clearTimeout(hideProc);
34336           clearTimeout(dismissProc);
34337           if(ce){
34338               hide(true);
34339           }
34340           locks.push(1);
34341        },
34342
34343     /**
34344      * Returns true if the quick tip is enabled, else false.
34345      */
34346        isEnabled : function(){
34347             return !disabled;
34348        },
34349
34350         // private
34351        tagConfig : {
34352            namespace : "roo", // was ext?? this may break..
34353            alt_namespace : "ext",
34354            attribute : "qtip",
34355            width : "width",
34356            target : "target",
34357            title : "qtitle",
34358            hide : "hide",
34359            cls : "qclass"
34360        }
34361    };
34362 }();
34363
34364 // backwards compat
34365 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34366  * Based on:
34367  * Ext JS Library 1.1.1
34368  * Copyright(c) 2006-2007, Ext JS, LLC.
34369  *
34370  * Originally Released Under LGPL - original licence link has changed is not relivant.
34371  *
34372  * Fork - LGPL
34373  * <script type="text/javascript">
34374  */
34375  
34376
34377 /**
34378  * @class Roo.tree.TreePanel
34379  * @extends Roo.data.Tree
34380  * @cfg {Roo.tree.TreeNode} root The root node
34381  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34382  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34383  * @cfg {Boolean} enableDD true to enable drag and drop
34384  * @cfg {Boolean} enableDrag true to enable just drag
34385  * @cfg {Boolean} enableDrop true to enable just drop
34386  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34387  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34388  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34389  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34390  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34391  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34392  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34393  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34394  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34395  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34396  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34397  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
34398  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
34399  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34400  * @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>
34401  * @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>
34402  * 
34403  * @constructor
34404  * @param {String/HTMLElement/Element} el The container element
34405  * @param {Object} config
34406  */
34407 Roo.tree.TreePanel = function(el, config){
34408     var root = false;
34409     var loader = false;
34410     if (config.root) {
34411         root = config.root;
34412         delete config.root;
34413     }
34414     if (config.loader) {
34415         loader = config.loader;
34416         delete config.loader;
34417     }
34418     
34419     Roo.apply(this, config);
34420     Roo.tree.TreePanel.superclass.constructor.call(this);
34421     this.el = Roo.get(el);
34422     this.el.addClass('x-tree');
34423     //console.log(root);
34424     if (root) {
34425         this.setRootNode( Roo.factory(root, Roo.tree));
34426     }
34427     if (loader) {
34428         this.loader = Roo.factory(loader, Roo.tree);
34429     }
34430    /**
34431     * Read-only. The id of the container element becomes this TreePanel's id.
34432     */
34433     this.id = this.el.id;
34434     this.addEvents({
34435         /**
34436         * @event beforeload
34437         * Fires before a node is loaded, return false to cancel
34438         * @param {Node} node The node being loaded
34439         */
34440         "beforeload" : true,
34441         /**
34442         * @event load
34443         * Fires when a node is loaded
34444         * @param {Node} node The node that was loaded
34445         */
34446         "load" : true,
34447         /**
34448         * @event textchange
34449         * Fires when the text for a node is changed
34450         * @param {Node} node The node
34451         * @param {String} text The new text
34452         * @param {String} oldText The old text
34453         */
34454         "textchange" : true,
34455         /**
34456         * @event beforeexpand
34457         * Fires before a node is expanded, return false to cancel.
34458         * @param {Node} node The node
34459         * @param {Boolean} deep
34460         * @param {Boolean} anim
34461         */
34462         "beforeexpand" : true,
34463         /**
34464         * @event beforecollapse
34465         * Fires before a node is collapsed, return false to cancel.
34466         * @param {Node} node The node
34467         * @param {Boolean} deep
34468         * @param {Boolean} anim
34469         */
34470         "beforecollapse" : true,
34471         /**
34472         * @event expand
34473         * Fires when a node is expanded
34474         * @param {Node} node The node
34475         */
34476         "expand" : true,
34477         /**
34478         * @event disabledchange
34479         * Fires when the disabled status of a node changes
34480         * @param {Node} node The node
34481         * @param {Boolean} disabled
34482         */
34483         "disabledchange" : true,
34484         /**
34485         * @event collapse
34486         * Fires when a node is collapsed
34487         * @param {Node} node The node
34488         */
34489         "collapse" : true,
34490         /**
34491         * @event beforeclick
34492         * Fires before click processing on a node. Return false to cancel the default action.
34493         * @param {Node} node The node
34494         * @param {Roo.EventObject} e The event object
34495         */
34496         "beforeclick":true,
34497         /**
34498         * @event checkchange
34499         * Fires when a node with a checkbox's checked property changes
34500         * @param {Node} this This node
34501         * @param {Boolean} checked
34502         */
34503         "checkchange":true,
34504         /**
34505         * @event click
34506         * Fires when a node is clicked
34507         * @param {Node} node The node
34508         * @param {Roo.EventObject} e The event object
34509         */
34510         "click":true,
34511         /**
34512         * @event dblclick
34513         * Fires when a node is double clicked
34514         * @param {Node} node The node
34515         * @param {Roo.EventObject} e The event object
34516         */
34517         "dblclick":true,
34518         /**
34519         * @event contextmenu
34520         * Fires when a node is right clicked
34521         * @param {Node} node The node
34522         * @param {Roo.EventObject} e The event object
34523         */
34524         "contextmenu":true,
34525         /**
34526         * @event beforechildrenrendered
34527         * Fires right before the child nodes for a node are rendered
34528         * @param {Node} node The node
34529         */
34530         "beforechildrenrendered":true,
34531         /**
34532         * @event startdrag
34533         * Fires when a node starts being dragged
34534         * @param {Roo.tree.TreePanel} this
34535         * @param {Roo.tree.TreeNode} node
34536         * @param {event} e The raw browser event
34537         */ 
34538        "startdrag" : true,
34539        /**
34540         * @event enddrag
34541         * Fires when a drag operation is complete
34542         * @param {Roo.tree.TreePanel} this
34543         * @param {Roo.tree.TreeNode} node
34544         * @param {event} e The raw browser event
34545         */
34546        "enddrag" : true,
34547        /**
34548         * @event dragdrop
34549         * Fires when a dragged node is dropped on a valid DD target
34550         * @param {Roo.tree.TreePanel} this
34551         * @param {Roo.tree.TreeNode} node
34552         * @param {DD} dd The dd it was dropped on
34553         * @param {event} e The raw browser event
34554         */
34555        "dragdrop" : true,
34556        /**
34557         * @event beforenodedrop
34558         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34559         * passed to handlers has the following properties:<br />
34560         * <ul style="padding:5px;padding-left:16px;">
34561         * <li>tree - The TreePanel</li>
34562         * <li>target - The node being targeted for the drop</li>
34563         * <li>data - The drag data from the drag source</li>
34564         * <li>point - The point of the drop - append, above or below</li>
34565         * <li>source - The drag source</li>
34566         * <li>rawEvent - Raw mouse event</li>
34567         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34568         * to be inserted by setting them on this object.</li>
34569         * <li>cancel - Set this to true to cancel the drop.</li>
34570         * </ul>
34571         * @param {Object} dropEvent
34572         */
34573        "beforenodedrop" : true,
34574        /**
34575         * @event nodedrop
34576         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34577         * passed to handlers has the following properties:<br />
34578         * <ul style="padding:5px;padding-left:16px;">
34579         * <li>tree - The TreePanel</li>
34580         * <li>target - The node being targeted for the drop</li>
34581         * <li>data - The drag data from the drag source</li>
34582         * <li>point - The point of the drop - append, above or below</li>
34583         * <li>source - The drag source</li>
34584         * <li>rawEvent - Raw mouse event</li>
34585         * <li>dropNode - Dropped node(s).</li>
34586         * </ul>
34587         * @param {Object} dropEvent
34588         */
34589        "nodedrop" : true,
34590         /**
34591         * @event nodedragover
34592         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34593         * passed to handlers has the following properties:<br />
34594         * <ul style="padding:5px;padding-left:16px;">
34595         * <li>tree - The TreePanel</li>
34596         * <li>target - The node being targeted for the drop</li>
34597         * <li>data - The drag data from the drag source</li>
34598         * <li>point - The point of the drop - append, above or below</li>
34599         * <li>source - The drag source</li>
34600         * <li>rawEvent - Raw mouse event</li>
34601         * <li>dropNode - Drop node(s) provided by the source.</li>
34602         * <li>cancel - Set this to true to signal drop not allowed.</li>
34603         * </ul>
34604         * @param {Object} dragOverEvent
34605         */
34606        "nodedragover" : true,
34607        /**
34608         * @event appendnode
34609         * Fires when append node to the tree
34610         * @param {Roo.tree.TreePanel} this
34611         * @param {Roo.tree.TreeNode} node
34612         * @param {Number} index The index of the newly appended node
34613         */
34614        "appendnode" : true
34615         
34616     });
34617     if(this.singleExpand){
34618        this.on("beforeexpand", this.restrictExpand, this);
34619     }
34620     if (this.editor) {
34621         this.editor.tree = this;
34622         this.editor = Roo.factory(this.editor, Roo.tree);
34623     }
34624     
34625     if (this.selModel) {
34626         this.selModel = Roo.factory(this.selModel, Roo.tree);
34627     }
34628    
34629 };
34630 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34631     rootVisible : true,
34632     animate: Roo.enableFx,
34633     lines : true,
34634     enableDD : false,
34635     hlDrop : Roo.enableFx,
34636   
34637     renderer: false,
34638     
34639     rendererTip: false,
34640     // private
34641     restrictExpand : function(node){
34642         var p = node.parentNode;
34643         if(p){
34644             if(p.expandedChild && p.expandedChild.parentNode == p){
34645                 p.expandedChild.collapse();
34646             }
34647             p.expandedChild = node;
34648         }
34649     },
34650
34651     // private override
34652     setRootNode : function(node){
34653         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34654         if(!this.rootVisible){
34655             node.ui = new Roo.tree.RootTreeNodeUI(node);
34656         }
34657         return node;
34658     },
34659
34660     /**
34661      * Returns the container element for this TreePanel
34662      */
34663     getEl : function(){
34664         return this.el;
34665     },
34666
34667     /**
34668      * Returns the default TreeLoader for this TreePanel
34669      */
34670     getLoader : function(){
34671         return this.loader;
34672     },
34673
34674     /**
34675      * Expand all nodes
34676      */
34677     expandAll : function(){
34678         this.root.expand(true);
34679     },
34680
34681     /**
34682      * Collapse all nodes
34683      */
34684     collapseAll : function(){
34685         this.root.collapse(true);
34686     },
34687
34688     /**
34689      * Returns the selection model used by this TreePanel
34690      */
34691     getSelectionModel : function(){
34692         if(!this.selModel){
34693             this.selModel = new Roo.tree.DefaultSelectionModel();
34694         }
34695         return this.selModel;
34696     },
34697
34698     /**
34699      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34700      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34701      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34702      * @return {Array}
34703      */
34704     getChecked : function(a, startNode){
34705         startNode = startNode || this.root;
34706         var r = [];
34707         var f = function(){
34708             if(this.attributes.checked){
34709                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34710             }
34711         }
34712         startNode.cascade(f);
34713         return r;
34714     },
34715
34716     /**
34717      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34718      * @param {String} path
34719      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34720      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34721      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34722      */
34723     expandPath : function(path, attr, callback){
34724         attr = attr || "id";
34725         var keys = path.split(this.pathSeparator);
34726         var curNode = this.root;
34727         if(curNode.attributes[attr] != keys[1]){ // invalid root
34728             if(callback){
34729                 callback(false, null);
34730             }
34731             return;
34732         }
34733         var index = 1;
34734         var f = function(){
34735             if(++index == keys.length){
34736                 if(callback){
34737                     callback(true, curNode);
34738                 }
34739                 return;
34740             }
34741             var c = curNode.findChild(attr, keys[index]);
34742             if(!c){
34743                 if(callback){
34744                     callback(false, curNode);
34745                 }
34746                 return;
34747             }
34748             curNode = c;
34749             c.expand(false, false, f);
34750         };
34751         curNode.expand(false, false, f);
34752     },
34753
34754     /**
34755      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34756      * @param {String} path
34757      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34758      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34759      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34760      */
34761     selectPath : function(path, attr, callback){
34762         attr = attr || "id";
34763         var keys = path.split(this.pathSeparator);
34764         var v = keys.pop();
34765         if(keys.length > 0){
34766             var f = function(success, node){
34767                 if(success && node){
34768                     var n = node.findChild(attr, v);
34769                     if(n){
34770                         n.select();
34771                         if(callback){
34772                             callback(true, n);
34773                         }
34774                     }else if(callback){
34775                         callback(false, n);
34776                     }
34777                 }else{
34778                     if(callback){
34779                         callback(false, n);
34780                     }
34781                 }
34782             };
34783             this.expandPath(keys.join(this.pathSeparator), attr, f);
34784         }else{
34785             this.root.select();
34786             if(callback){
34787                 callback(true, this.root);
34788             }
34789         }
34790     },
34791
34792     getTreeEl : function(){
34793         return this.el;
34794     },
34795
34796     /**
34797      * Trigger rendering of this TreePanel
34798      */
34799     render : function(){
34800         if (this.innerCt) {
34801             return this; // stop it rendering more than once!!
34802         }
34803         
34804         this.innerCt = this.el.createChild({tag:"ul",
34805                cls:"x-tree-root-ct " +
34806                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34807
34808         if(this.containerScroll){
34809             Roo.dd.ScrollManager.register(this.el);
34810         }
34811         if((this.enableDD || this.enableDrop) && !this.dropZone){
34812            /**
34813             * The dropZone used by this tree if drop is enabled
34814             * @type Roo.tree.TreeDropZone
34815             */
34816              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34817                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34818            });
34819         }
34820         if((this.enableDD || this.enableDrag) && !this.dragZone){
34821            /**
34822             * The dragZone used by this tree if drag is enabled
34823             * @type Roo.tree.TreeDragZone
34824             */
34825             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34826                ddGroup: this.ddGroup || "TreeDD",
34827                scroll: this.ddScroll
34828            });
34829         }
34830         this.getSelectionModel().init(this);
34831         if (!this.root) {
34832             Roo.log("ROOT not set in tree");
34833             return this;
34834         }
34835         this.root.render();
34836         if(!this.rootVisible){
34837             this.root.renderChildren();
34838         }
34839         return this;
34840     }
34841 });/*
34842  * Based on:
34843  * Ext JS Library 1.1.1
34844  * Copyright(c) 2006-2007, Ext JS, LLC.
34845  *
34846  * Originally Released Under LGPL - original licence link has changed is not relivant.
34847  *
34848  * Fork - LGPL
34849  * <script type="text/javascript">
34850  */
34851  
34852
34853 /**
34854  * @class Roo.tree.DefaultSelectionModel
34855  * @extends Roo.util.Observable
34856  * The default single selection for a TreePanel.
34857  * @param {Object} cfg Configuration
34858  */
34859 Roo.tree.DefaultSelectionModel = function(cfg){
34860    this.selNode = null;
34861    
34862    
34863    
34864    this.addEvents({
34865        /**
34866         * @event selectionchange
34867         * Fires when the selected node changes
34868         * @param {DefaultSelectionModel} this
34869         * @param {TreeNode} node the new selection
34870         */
34871        "selectionchange" : true,
34872
34873        /**
34874         * @event beforeselect
34875         * Fires before the selected node changes, return false to cancel the change
34876         * @param {DefaultSelectionModel} this
34877         * @param {TreeNode} node the new selection
34878         * @param {TreeNode} node the old selection
34879         */
34880        "beforeselect" : true
34881    });
34882    
34883     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34884 };
34885
34886 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34887     init : function(tree){
34888         this.tree = tree;
34889         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34890         tree.on("click", this.onNodeClick, this);
34891     },
34892     
34893     onNodeClick : function(node, e){
34894         if (e.ctrlKey && this.selNode == node)  {
34895             this.unselect(node);
34896             return;
34897         }
34898         this.select(node);
34899     },
34900     
34901     /**
34902      * Select a node.
34903      * @param {TreeNode} node The node to select
34904      * @return {TreeNode} The selected node
34905      */
34906     select : function(node){
34907         var last = this.selNode;
34908         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34909             if(last){
34910                 last.ui.onSelectedChange(false);
34911             }
34912             this.selNode = node;
34913             node.ui.onSelectedChange(true);
34914             this.fireEvent("selectionchange", this, node, last);
34915         }
34916         return node;
34917     },
34918     
34919     /**
34920      * Deselect a node.
34921      * @param {TreeNode} node The node to unselect
34922      */
34923     unselect : function(node){
34924         if(this.selNode == node){
34925             this.clearSelections();
34926         }    
34927     },
34928     
34929     /**
34930      * Clear all selections
34931      */
34932     clearSelections : function(){
34933         var n = this.selNode;
34934         if(n){
34935             n.ui.onSelectedChange(false);
34936             this.selNode = null;
34937             this.fireEvent("selectionchange", this, null);
34938         }
34939         return n;
34940     },
34941     
34942     /**
34943      * Get the selected node
34944      * @return {TreeNode} The selected node
34945      */
34946     getSelectedNode : function(){
34947         return this.selNode;    
34948     },
34949     
34950     /**
34951      * Returns true if the node is selected
34952      * @param {TreeNode} node The node to check
34953      * @return {Boolean}
34954      */
34955     isSelected : function(node){
34956         return this.selNode == node;  
34957     },
34958
34959     /**
34960      * Selects the node above the selected node in the tree, intelligently walking the nodes
34961      * @return TreeNode The new selection
34962      */
34963     selectPrevious : function(){
34964         var s = this.selNode || this.lastSelNode;
34965         if(!s){
34966             return null;
34967         }
34968         var ps = s.previousSibling;
34969         if(ps){
34970             if(!ps.isExpanded() || ps.childNodes.length < 1){
34971                 return this.select(ps);
34972             } else{
34973                 var lc = ps.lastChild;
34974                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34975                     lc = lc.lastChild;
34976                 }
34977                 return this.select(lc);
34978             }
34979         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34980             return this.select(s.parentNode);
34981         }
34982         return null;
34983     },
34984
34985     /**
34986      * Selects the node above the selected node in the tree, intelligently walking the nodes
34987      * @return TreeNode The new selection
34988      */
34989     selectNext : function(){
34990         var s = this.selNode || this.lastSelNode;
34991         if(!s){
34992             return null;
34993         }
34994         if(s.firstChild && s.isExpanded()){
34995              return this.select(s.firstChild);
34996          }else if(s.nextSibling){
34997              return this.select(s.nextSibling);
34998          }else if(s.parentNode){
34999             var newS = null;
35000             s.parentNode.bubble(function(){
35001                 if(this.nextSibling){
35002                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35003                     return false;
35004                 }
35005             });
35006             return newS;
35007          }
35008         return null;
35009     },
35010
35011     onKeyDown : function(e){
35012         var s = this.selNode || this.lastSelNode;
35013         // undesirable, but required
35014         var sm = this;
35015         if(!s){
35016             return;
35017         }
35018         var k = e.getKey();
35019         switch(k){
35020              case e.DOWN:
35021                  e.stopEvent();
35022                  this.selectNext();
35023              break;
35024              case e.UP:
35025                  e.stopEvent();
35026                  this.selectPrevious();
35027              break;
35028              case e.RIGHT:
35029                  e.preventDefault();
35030                  if(s.hasChildNodes()){
35031                      if(!s.isExpanded()){
35032                          s.expand();
35033                      }else if(s.firstChild){
35034                          this.select(s.firstChild, e);
35035                      }
35036                  }
35037              break;
35038              case e.LEFT:
35039                  e.preventDefault();
35040                  if(s.hasChildNodes() && s.isExpanded()){
35041                      s.collapse();
35042                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35043                      this.select(s.parentNode, e);
35044                  }
35045              break;
35046         };
35047     }
35048 });
35049
35050 /**
35051  * @class Roo.tree.MultiSelectionModel
35052  * @extends Roo.util.Observable
35053  * Multi selection for a TreePanel.
35054  * @param {Object} cfg Configuration
35055  */
35056 Roo.tree.MultiSelectionModel = function(){
35057    this.selNodes = [];
35058    this.selMap = {};
35059    this.addEvents({
35060        /**
35061         * @event selectionchange
35062         * Fires when the selected nodes change
35063         * @param {MultiSelectionModel} this
35064         * @param {Array} nodes Array of the selected nodes
35065         */
35066        "selectionchange" : true
35067    });
35068    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35069    
35070 };
35071
35072 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35073     init : function(tree){
35074         this.tree = tree;
35075         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35076         tree.on("click", this.onNodeClick, this);
35077     },
35078     
35079     onNodeClick : function(node, e){
35080         this.select(node, e, e.ctrlKey);
35081     },
35082     
35083     /**
35084      * Select a node.
35085      * @param {TreeNode} node The node to select
35086      * @param {EventObject} e (optional) An event associated with the selection
35087      * @param {Boolean} keepExisting True to retain existing selections
35088      * @return {TreeNode} The selected node
35089      */
35090     select : function(node, e, keepExisting){
35091         if(keepExisting !== true){
35092             this.clearSelections(true);
35093         }
35094         if(this.isSelected(node)){
35095             this.lastSelNode = node;
35096             return node;
35097         }
35098         this.selNodes.push(node);
35099         this.selMap[node.id] = node;
35100         this.lastSelNode = node;
35101         node.ui.onSelectedChange(true);
35102         this.fireEvent("selectionchange", this, this.selNodes);
35103         return node;
35104     },
35105     
35106     /**
35107      * Deselect a node.
35108      * @param {TreeNode} node The node to unselect
35109      */
35110     unselect : function(node){
35111         if(this.selMap[node.id]){
35112             node.ui.onSelectedChange(false);
35113             var sn = this.selNodes;
35114             var index = -1;
35115             if(sn.indexOf){
35116                 index = sn.indexOf(node);
35117             }else{
35118                 for(var i = 0, len = sn.length; i < len; i++){
35119                     if(sn[i] == node){
35120                         index = i;
35121                         break;
35122                     }
35123                 }
35124             }
35125             if(index != -1){
35126                 this.selNodes.splice(index, 1);
35127             }
35128             delete this.selMap[node.id];
35129             this.fireEvent("selectionchange", this, this.selNodes);
35130         }
35131     },
35132     
35133     /**
35134      * Clear all selections
35135      */
35136     clearSelections : function(suppressEvent){
35137         var sn = this.selNodes;
35138         if(sn.length > 0){
35139             for(var i = 0, len = sn.length; i < len; i++){
35140                 sn[i].ui.onSelectedChange(false);
35141             }
35142             this.selNodes = [];
35143             this.selMap = {};
35144             if(suppressEvent !== true){
35145                 this.fireEvent("selectionchange", this, this.selNodes);
35146             }
35147         }
35148     },
35149     
35150     /**
35151      * Returns true if the node is selected
35152      * @param {TreeNode} node The node to check
35153      * @return {Boolean}
35154      */
35155     isSelected : function(node){
35156         return this.selMap[node.id] ? true : false;  
35157     },
35158     
35159     /**
35160      * Returns an array of the selected nodes
35161      * @return {Array}
35162      */
35163     getSelectedNodes : function(){
35164         return this.selNodes;    
35165     },
35166
35167     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35168
35169     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35170
35171     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35172 });/*
35173  * Based on:
35174  * Ext JS Library 1.1.1
35175  * Copyright(c) 2006-2007, Ext JS, LLC.
35176  *
35177  * Originally Released Under LGPL - original licence link has changed is not relivant.
35178  *
35179  * Fork - LGPL
35180  * <script type="text/javascript">
35181  */
35182  
35183 /**
35184  * @class Roo.tree.TreeNode
35185  * @extends Roo.data.Node
35186  * @cfg {String} text The text for this node
35187  * @cfg {Boolean} expanded true to start the node expanded
35188  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35189  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35190  * @cfg {Boolean} disabled true to start the node disabled
35191  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35192  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35193  * @cfg {String} cls A css class to be added to the node
35194  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35195  * @cfg {String} href URL of the link used for the node (defaults to #)
35196  * @cfg {String} hrefTarget target frame for the link
35197  * @cfg {String} qtip An Ext QuickTip for the node
35198  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35199  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35200  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35201  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35202  * (defaults to undefined with no checkbox rendered)
35203  * @constructor
35204  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35205  */
35206 Roo.tree.TreeNode = function(attributes){
35207     attributes = attributes || {};
35208     if(typeof attributes == "string"){
35209         attributes = {text: attributes};
35210     }
35211     this.childrenRendered = false;
35212     this.rendered = false;
35213     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35214     this.expanded = attributes.expanded === true;
35215     this.isTarget = attributes.isTarget !== false;
35216     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35217     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35218
35219     /**
35220      * Read-only. The text for this node. To change it use setText().
35221      * @type String
35222      */
35223     this.text = attributes.text;
35224     /**
35225      * True if this node is disabled.
35226      * @type Boolean
35227      */
35228     this.disabled = attributes.disabled === true;
35229
35230     this.addEvents({
35231         /**
35232         * @event textchange
35233         * Fires when the text for this node is changed
35234         * @param {Node} this This node
35235         * @param {String} text The new text
35236         * @param {String} oldText The old text
35237         */
35238         "textchange" : true,
35239         /**
35240         * @event beforeexpand
35241         * Fires before this node is expanded, return false to cancel.
35242         * @param {Node} this This node
35243         * @param {Boolean} deep
35244         * @param {Boolean} anim
35245         */
35246         "beforeexpand" : true,
35247         /**
35248         * @event beforecollapse
35249         * Fires before this node is collapsed, return false to cancel.
35250         * @param {Node} this This node
35251         * @param {Boolean} deep
35252         * @param {Boolean} anim
35253         */
35254         "beforecollapse" : true,
35255         /**
35256         * @event expand
35257         * Fires when this node is expanded
35258         * @param {Node} this This node
35259         */
35260         "expand" : true,
35261         /**
35262         * @event disabledchange
35263         * Fires when the disabled status of this node changes
35264         * @param {Node} this This node
35265         * @param {Boolean} disabled
35266         */
35267         "disabledchange" : true,
35268         /**
35269         * @event collapse
35270         * Fires when this node is collapsed
35271         * @param {Node} this This node
35272         */
35273         "collapse" : true,
35274         /**
35275         * @event beforeclick
35276         * Fires before click processing. Return false to cancel the default action.
35277         * @param {Node} this This node
35278         * @param {Roo.EventObject} e The event object
35279         */
35280         "beforeclick":true,
35281         /**
35282         * @event checkchange
35283         * Fires when a node with a checkbox's checked property changes
35284         * @param {Node} this This node
35285         * @param {Boolean} checked
35286         */
35287         "checkchange":true,
35288         /**
35289         * @event click
35290         * Fires when this node is clicked
35291         * @param {Node} this This node
35292         * @param {Roo.EventObject} e The event object
35293         */
35294         "click":true,
35295         /**
35296         * @event dblclick
35297         * Fires when this node is double clicked
35298         * @param {Node} this This node
35299         * @param {Roo.EventObject} e The event object
35300         */
35301         "dblclick":true,
35302         /**
35303         * @event contextmenu
35304         * Fires when this node is right clicked
35305         * @param {Node} this This node
35306         * @param {Roo.EventObject} e The event object
35307         */
35308         "contextmenu":true,
35309         /**
35310         * @event beforechildrenrendered
35311         * Fires right before the child nodes for this node are rendered
35312         * @param {Node} this This node
35313         */
35314         "beforechildrenrendered":true
35315     });
35316
35317     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35318
35319     /**
35320      * Read-only. The UI for this node
35321      * @type TreeNodeUI
35322      */
35323     this.ui = new uiClass(this);
35324     
35325     // finally support items[]
35326     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35327         return;
35328     }
35329     
35330     
35331     Roo.each(this.attributes.items, function(c) {
35332         this.appendChild(Roo.factory(c,Roo.Tree));
35333     }, this);
35334     delete this.attributes.items;
35335     
35336     
35337     
35338 };
35339 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35340     preventHScroll: true,
35341     /**
35342      * Returns true if this node is expanded
35343      * @return {Boolean}
35344      */
35345     isExpanded : function(){
35346         return this.expanded;
35347     },
35348
35349     /**
35350      * Returns the UI object for this node
35351      * @return {TreeNodeUI}
35352      */
35353     getUI : function(){
35354         return this.ui;
35355     },
35356
35357     // private override
35358     setFirstChild : function(node){
35359         var of = this.firstChild;
35360         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35361         if(this.childrenRendered && of && node != of){
35362             of.renderIndent(true, true);
35363         }
35364         if(this.rendered){
35365             this.renderIndent(true, true);
35366         }
35367     },
35368
35369     // private override
35370     setLastChild : function(node){
35371         var ol = this.lastChild;
35372         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35373         if(this.childrenRendered && ol && node != ol){
35374             ol.renderIndent(true, true);
35375         }
35376         if(this.rendered){
35377             this.renderIndent(true, true);
35378         }
35379     },
35380
35381     // these methods are overridden to provide lazy rendering support
35382     // private override
35383     appendChild : function()
35384     {
35385         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35386         if(node && this.childrenRendered){
35387             node.render();
35388         }
35389         this.ui.updateExpandIcon();
35390         return node;
35391     },
35392
35393     // private override
35394     removeChild : function(node){
35395         this.ownerTree.getSelectionModel().unselect(node);
35396         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35397         // if it's been rendered remove dom node
35398         if(this.childrenRendered){
35399             node.ui.remove();
35400         }
35401         if(this.childNodes.length < 1){
35402             this.collapse(false, false);
35403         }else{
35404             this.ui.updateExpandIcon();
35405         }
35406         if(!this.firstChild) {
35407             this.childrenRendered = false;
35408         }
35409         return node;
35410     },
35411
35412     // private override
35413     insertBefore : function(node, refNode){
35414         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35415         if(newNode && refNode && this.childrenRendered){
35416             node.render();
35417         }
35418         this.ui.updateExpandIcon();
35419         return newNode;
35420     },
35421
35422     /**
35423      * Sets the text for this node
35424      * @param {String} text
35425      */
35426     setText : function(text){
35427         var oldText = this.text;
35428         this.text = text;
35429         this.attributes.text = text;
35430         if(this.rendered){ // event without subscribing
35431             this.ui.onTextChange(this, text, oldText);
35432         }
35433         this.fireEvent("textchange", this, text, oldText);
35434     },
35435
35436     /**
35437      * Triggers selection of this node
35438      */
35439     select : function(){
35440         this.getOwnerTree().getSelectionModel().select(this);
35441     },
35442
35443     /**
35444      * Triggers deselection of this node
35445      */
35446     unselect : function(){
35447         this.getOwnerTree().getSelectionModel().unselect(this);
35448     },
35449
35450     /**
35451      * Returns true if this node is selected
35452      * @return {Boolean}
35453      */
35454     isSelected : function(){
35455         return this.getOwnerTree().getSelectionModel().isSelected(this);
35456     },
35457
35458     /**
35459      * Expand this node.
35460      * @param {Boolean} deep (optional) True to expand all children as well
35461      * @param {Boolean} anim (optional) false to cancel the default animation
35462      * @param {Function} callback (optional) A callback to be called when
35463      * expanding this node completes (does not wait for deep expand to complete).
35464      * Called with 1 parameter, this node.
35465      */
35466     expand : function(deep, anim, callback){
35467         if(!this.expanded){
35468             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35469                 return;
35470             }
35471             if(!this.childrenRendered){
35472                 this.renderChildren();
35473             }
35474             this.expanded = true;
35475             
35476             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35477                 this.ui.animExpand(function(){
35478                     this.fireEvent("expand", this);
35479                     if(typeof callback == "function"){
35480                         callback(this);
35481                     }
35482                     if(deep === true){
35483                         this.expandChildNodes(true);
35484                     }
35485                 }.createDelegate(this));
35486                 return;
35487             }else{
35488                 this.ui.expand();
35489                 this.fireEvent("expand", this);
35490                 if(typeof callback == "function"){
35491                     callback(this);
35492                 }
35493             }
35494         }else{
35495            if(typeof callback == "function"){
35496                callback(this);
35497            }
35498         }
35499         if(deep === true){
35500             this.expandChildNodes(true);
35501         }
35502     },
35503
35504     isHiddenRoot : function(){
35505         return this.isRoot && !this.getOwnerTree().rootVisible;
35506     },
35507
35508     /**
35509      * Collapse this node.
35510      * @param {Boolean} deep (optional) True to collapse all children as well
35511      * @param {Boolean} anim (optional) false to cancel the default animation
35512      */
35513     collapse : function(deep, anim){
35514         if(this.expanded && !this.isHiddenRoot()){
35515             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35516                 return;
35517             }
35518             this.expanded = false;
35519             if((this.getOwnerTree().animate && anim !== false) || anim){
35520                 this.ui.animCollapse(function(){
35521                     this.fireEvent("collapse", this);
35522                     if(deep === true){
35523                         this.collapseChildNodes(true);
35524                     }
35525                 }.createDelegate(this));
35526                 return;
35527             }else{
35528                 this.ui.collapse();
35529                 this.fireEvent("collapse", this);
35530             }
35531         }
35532         if(deep === true){
35533             var cs = this.childNodes;
35534             for(var i = 0, len = cs.length; i < len; i++) {
35535                 cs[i].collapse(true, false);
35536             }
35537         }
35538     },
35539
35540     // private
35541     delayedExpand : function(delay){
35542         if(!this.expandProcId){
35543             this.expandProcId = this.expand.defer(delay, this);
35544         }
35545     },
35546
35547     // private
35548     cancelExpand : function(){
35549         if(this.expandProcId){
35550             clearTimeout(this.expandProcId);
35551         }
35552         this.expandProcId = false;
35553     },
35554
35555     /**
35556      * Toggles expanded/collapsed state of the node
35557      */
35558     toggle : function(){
35559         if(this.expanded){
35560             this.collapse();
35561         }else{
35562             this.expand();
35563         }
35564     },
35565
35566     /**
35567      * Ensures all parent nodes are expanded
35568      */
35569     ensureVisible : function(callback){
35570         var tree = this.getOwnerTree();
35571         tree.expandPath(this.parentNode.getPath(), false, function(){
35572             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35573             Roo.callback(callback);
35574         }.createDelegate(this));
35575     },
35576
35577     /**
35578      * Expand all child nodes
35579      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35580      */
35581     expandChildNodes : function(deep){
35582         var cs = this.childNodes;
35583         for(var i = 0, len = cs.length; i < len; i++) {
35584                 cs[i].expand(deep);
35585         }
35586     },
35587
35588     /**
35589      * Collapse all child nodes
35590      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35591      */
35592     collapseChildNodes : function(deep){
35593         var cs = this.childNodes;
35594         for(var i = 0, len = cs.length; i < len; i++) {
35595                 cs[i].collapse(deep);
35596         }
35597     },
35598
35599     /**
35600      * Disables this node
35601      */
35602     disable : function(){
35603         this.disabled = true;
35604         this.unselect();
35605         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35606             this.ui.onDisableChange(this, true);
35607         }
35608         this.fireEvent("disabledchange", this, true);
35609     },
35610
35611     /**
35612      * Enables this node
35613      */
35614     enable : function(){
35615         this.disabled = false;
35616         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35617             this.ui.onDisableChange(this, false);
35618         }
35619         this.fireEvent("disabledchange", this, false);
35620     },
35621
35622     // private
35623     renderChildren : function(suppressEvent){
35624         if(suppressEvent !== false){
35625             this.fireEvent("beforechildrenrendered", this);
35626         }
35627         var cs = this.childNodes;
35628         for(var i = 0, len = cs.length; i < len; i++){
35629             cs[i].render(true);
35630         }
35631         this.childrenRendered = true;
35632     },
35633
35634     // private
35635     sort : function(fn, scope){
35636         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35637         if(this.childrenRendered){
35638             var cs = this.childNodes;
35639             for(var i = 0, len = cs.length; i < len; i++){
35640                 cs[i].render(true);
35641             }
35642         }
35643     },
35644
35645     // private
35646     render : function(bulkRender){
35647         this.ui.render(bulkRender);
35648         if(!this.rendered){
35649             this.rendered = true;
35650             if(this.expanded){
35651                 this.expanded = false;
35652                 this.expand(false, false);
35653             }
35654         }
35655     },
35656
35657     // private
35658     renderIndent : function(deep, refresh){
35659         if(refresh){
35660             this.ui.childIndent = null;
35661         }
35662         this.ui.renderIndent();
35663         if(deep === true && this.childrenRendered){
35664             var cs = this.childNodes;
35665             for(var i = 0, len = cs.length; i < len; i++){
35666                 cs[i].renderIndent(true, refresh);
35667             }
35668         }
35669     }
35670 });/*
35671  * Based on:
35672  * Ext JS Library 1.1.1
35673  * Copyright(c) 2006-2007, Ext JS, LLC.
35674  *
35675  * Originally Released Under LGPL - original licence link has changed is not relivant.
35676  *
35677  * Fork - LGPL
35678  * <script type="text/javascript">
35679  */
35680  
35681 /**
35682  * @class Roo.tree.AsyncTreeNode
35683  * @extends Roo.tree.TreeNode
35684  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35685  * @constructor
35686  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35687  */
35688  Roo.tree.AsyncTreeNode = function(config){
35689     this.loaded = false;
35690     this.loading = false;
35691     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35692     /**
35693     * @event beforeload
35694     * Fires before this node is loaded, return false to cancel
35695     * @param {Node} this This node
35696     */
35697     this.addEvents({'beforeload':true, 'load': true});
35698     /**
35699     * @event load
35700     * Fires when this node is loaded
35701     * @param {Node} this This node
35702     */
35703     /**
35704      * The loader used by this node (defaults to using the tree's defined loader)
35705      * @type TreeLoader
35706      * @property loader
35707      */
35708 };
35709 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35710     expand : function(deep, anim, callback){
35711         if(this.loading){ // if an async load is already running, waiting til it's done
35712             var timer;
35713             var f = function(){
35714                 if(!this.loading){ // done loading
35715                     clearInterval(timer);
35716                     this.expand(deep, anim, callback);
35717                 }
35718             }.createDelegate(this);
35719             timer = setInterval(f, 200);
35720             return;
35721         }
35722         if(!this.loaded){
35723             if(this.fireEvent("beforeload", this) === false){
35724                 return;
35725             }
35726             this.loading = true;
35727             this.ui.beforeLoad(this);
35728             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35729             if(loader){
35730                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35731                 return;
35732             }
35733         }
35734         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35735     },
35736     
35737     /**
35738      * Returns true if this node is currently loading
35739      * @return {Boolean}
35740      */
35741     isLoading : function(){
35742         return this.loading;  
35743     },
35744     
35745     loadComplete : function(deep, anim, callback){
35746         this.loading = false;
35747         this.loaded = true;
35748         this.ui.afterLoad(this);
35749         this.fireEvent("load", this);
35750         this.expand(deep, anim, callback);
35751     },
35752     
35753     /**
35754      * Returns true if this node has been loaded
35755      * @return {Boolean}
35756      */
35757     isLoaded : function(){
35758         return this.loaded;
35759     },
35760     
35761     hasChildNodes : function(){
35762         if(!this.isLeaf() && !this.loaded){
35763             return true;
35764         }else{
35765             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35766         }
35767     },
35768
35769     /**
35770      * Trigger a reload for this node
35771      * @param {Function} callback
35772      */
35773     reload : function(callback){
35774         this.collapse(false, false);
35775         while(this.firstChild){
35776             this.removeChild(this.firstChild);
35777         }
35778         this.childrenRendered = false;
35779         this.loaded = false;
35780         if(this.isHiddenRoot()){
35781             this.expanded = false;
35782         }
35783         this.expand(false, false, callback);
35784     }
35785 });/*
35786  * Based on:
35787  * Ext JS Library 1.1.1
35788  * Copyright(c) 2006-2007, Ext JS, LLC.
35789  *
35790  * Originally Released Under LGPL - original licence link has changed is not relivant.
35791  *
35792  * Fork - LGPL
35793  * <script type="text/javascript">
35794  */
35795  
35796 /**
35797  * @class Roo.tree.TreeNodeUI
35798  * @constructor
35799  * @param {Object} node The node to render
35800  * The TreeNode UI implementation is separate from the
35801  * tree implementation. Unless you are customizing the tree UI,
35802  * you should never have to use this directly.
35803  */
35804 Roo.tree.TreeNodeUI = function(node){
35805     this.node = node;
35806     this.rendered = false;
35807     this.animating = false;
35808     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35809 };
35810
35811 Roo.tree.TreeNodeUI.prototype = {
35812     removeChild : function(node){
35813         if(this.rendered){
35814             this.ctNode.removeChild(node.ui.getEl());
35815         }
35816     },
35817
35818     beforeLoad : function(){
35819          this.addClass("x-tree-node-loading");
35820     },
35821
35822     afterLoad : function(){
35823          this.removeClass("x-tree-node-loading");
35824     },
35825
35826     onTextChange : function(node, text, oldText){
35827         if(this.rendered){
35828             this.textNode.innerHTML = text;
35829         }
35830     },
35831
35832     onDisableChange : function(node, state){
35833         this.disabled = state;
35834         if(state){
35835             this.addClass("x-tree-node-disabled");
35836         }else{
35837             this.removeClass("x-tree-node-disabled");
35838         }
35839     },
35840
35841     onSelectedChange : function(state){
35842         if(state){
35843             this.focus();
35844             this.addClass("x-tree-selected");
35845         }else{
35846             //this.blur();
35847             this.removeClass("x-tree-selected");
35848         }
35849     },
35850
35851     onMove : function(tree, node, oldParent, newParent, index, refNode){
35852         this.childIndent = null;
35853         if(this.rendered){
35854             var targetNode = newParent.ui.getContainer();
35855             if(!targetNode){//target not rendered
35856                 this.holder = document.createElement("div");
35857                 this.holder.appendChild(this.wrap);
35858                 return;
35859             }
35860             var insertBefore = refNode ? refNode.ui.getEl() : null;
35861             if(insertBefore){
35862                 targetNode.insertBefore(this.wrap, insertBefore);
35863             }else{
35864                 targetNode.appendChild(this.wrap);
35865             }
35866             this.node.renderIndent(true);
35867         }
35868     },
35869
35870     addClass : function(cls){
35871         if(this.elNode){
35872             Roo.fly(this.elNode).addClass(cls);
35873         }
35874     },
35875
35876     removeClass : function(cls){
35877         if(this.elNode){
35878             Roo.fly(this.elNode).removeClass(cls);
35879         }
35880     },
35881
35882     remove : function(){
35883         if(this.rendered){
35884             this.holder = document.createElement("div");
35885             this.holder.appendChild(this.wrap);
35886         }
35887     },
35888
35889     fireEvent : function(){
35890         return this.node.fireEvent.apply(this.node, arguments);
35891     },
35892
35893     initEvents : function(){
35894         this.node.on("move", this.onMove, this);
35895         var E = Roo.EventManager;
35896         var a = this.anchor;
35897
35898         var el = Roo.fly(a, '_treeui');
35899
35900         if(Roo.isOpera){ // opera render bug ignores the CSS
35901             el.setStyle("text-decoration", "none");
35902         }
35903
35904         el.on("click", this.onClick, this);
35905         el.on("dblclick", this.onDblClick, this);
35906
35907         if(this.checkbox){
35908             Roo.EventManager.on(this.checkbox,
35909                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35910         }
35911
35912         el.on("contextmenu", this.onContextMenu, this);
35913
35914         var icon = Roo.fly(this.iconNode);
35915         icon.on("click", this.onClick, this);
35916         icon.on("dblclick", this.onDblClick, this);
35917         icon.on("contextmenu", this.onContextMenu, this);
35918         E.on(this.ecNode, "click", this.ecClick, this, true);
35919
35920         if(this.node.disabled){
35921             this.addClass("x-tree-node-disabled");
35922         }
35923         if(this.node.hidden){
35924             this.addClass("x-tree-node-disabled");
35925         }
35926         var ot = this.node.getOwnerTree();
35927         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35928         if(dd && (!this.node.isRoot || ot.rootVisible)){
35929             Roo.dd.Registry.register(this.elNode, {
35930                 node: this.node,
35931                 handles: this.getDDHandles(),
35932                 isHandle: false
35933             });
35934         }
35935     },
35936
35937     getDDHandles : function(){
35938         return [this.iconNode, this.textNode];
35939     },
35940
35941     hide : function(){
35942         if(this.rendered){
35943             this.wrap.style.display = "none";
35944         }
35945     },
35946
35947     show : function(){
35948         if(this.rendered){
35949             this.wrap.style.display = "";
35950         }
35951     },
35952
35953     onContextMenu : function(e){
35954         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35955             e.preventDefault();
35956             this.focus();
35957             this.fireEvent("contextmenu", this.node, e);
35958         }
35959     },
35960
35961     onClick : function(e){
35962         if(this.dropping){
35963             e.stopEvent();
35964             return;
35965         }
35966         if(this.fireEvent("beforeclick", this.node, e) !== false){
35967             if(!this.disabled && this.node.attributes.href){
35968                 this.fireEvent("click", this.node, e);
35969                 return;
35970             }
35971             e.preventDefault();
35972             if(this.disabled){
35973                 return;
35974             }
35975
35976             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35977                 this.node.toggle();
35978             }
35979
35980             this.fireEvent("click", this.node, e);
35981         }else{
35982             e.stopEvent();
35983         }
35984     },
35985
35986     onDblClick : function(e){
35987         e.preventDefault();
35988         if(this.disabled){
35989             return;
35990         }
35991         if(this.checkbox){
35992             this.toggleCheck();
35993         }
35994         if(!this.animating && this.node.hasChildNodes()){
35995             this.node.toggle();
35996         }
35997         this.fireEvent("dblclick", this.node, e);
35998     },
35999
36000     onCheckChange : function(){
36001         var checked = this.checkbox.checked;
36002         this.node.attributes.checked = checked;
36003         this.fireEvent('checkchange', this.node, checked);
36004     },
36005
36006     ecClick : function(e){
36007         if(!this.animating && this.node.hasChildNodes()){
36008             this.node.toggle();
36009         }
36010     },
36011
36012     startDrop : function(){
36013         this.dropping = true;
36014     },
36015
36016     // delayed drop so the click event doesn't get fired on a drop
36017     endDrop : function(){
36018        setTimeout(function(){
36019            this.dropping = false;
36020        }.createDelegate(this), 50);
36021     },
36022
36023     expand : function(){
36024         this.updateExpandIcon();
36025         this.ctNode.style.display = "";
36026     },
36027
36028     focus : function(){
36029         if(!this.node.preventHScroll){
36030             try{this.anchor.focus();
36031             }catch(e){}
36032         }else if(!Roo.isIE){
36033             try{
36034                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36035                 var l = noscroll.scrollLeft;
36036                 this.anchor.focus();
36037                 noscroll.scrollLeft = l;
36038             }catch(e){}
36039         }
36040     },
36041
36042     toggleCheck : function(value){
36043         var cb = this.checkbox;
36044         if(cb){
36045             cb.checked = (value === undefined ? !cb.checked : value);
36046         }
36047     },
36048
36049     blur : function(){
36050         try{
36051             this.anchor.blur();
36052         }catch(e){}
36053     },
36054
36055     animExpand : function(callback){
36056         var ct = Roo.get(this.ctNode);
36057         ct.stopFx();
36058         if(!this.node.hasChildNodes()){
36059             this.updateExpandIcon();
36060             this.ctNode.style.display = "";
36061             Roo.callback(callback);
36062             return;
36063         }
36064         this.animating = true;
36065         this.updateExpandIcon();
36066
36067         ct.slideIn('t', {
36068            callback : function(){
36069                this.animating = false;
36070                Roo.callback(callback);
36071             },
36072             scope: this,
36073             duration: this.node.ownerTree.duration || .25
36074         });
36075     },
36076
36077     highlight : function(){
36078         var tree = this.node.getOwnerTree();
36079         Roo.fly(this.wrap).highlight(
36080             tree.hlColor || "C3DAF9",
36081             {endColor: tree.hlBaseColor}
36082         );
36083     },
36084
36085     collapse : function(){
36086         this.updateExpandIcon();
36087         this.ctNode.style.display = "none";
36088     },
36089
36090     animCollapse : function(callback){
36091         var ct = Roo.get(this.ctNode);
36092         ct.enableDisplayMode('block');
36093         ct.stopFx();
36094
36095         this.animating = true;
36096         this.updateExpandIcon();
36097
36098         ct.slideOut('t', {
36099             callback : function(){
36100                this.animating = false;
36101                Roo.callback(callback);
36102             },
36103             scope: this,
36104             duration: this.node.ownerTree.duration || .25
36105         });
36106     },
36107
36108     getContainer : function(){
36109         return this.ctNode;
36110     },
36111
36112     getEl : function(){
36113         return this.wrap;
36114     },
36115
36116     appendDDGhost : function(ghostNode){
36117         ghostNode.appendChild(this.elNode.cloneNode(true));
36118     },
36119
36120     getDDRepairXY : function(){
36121         return Roo.lib.Dom.getXY(this.iconNode);
36122     },
36123
36124     onRender : function(){
36125         this.render();
36126     },
36127
36128     render : function(bulkRender){
36129         var n = this.node, a = n.attributes;
36130         var targetNode = n.parentNode ?
36131               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36132
36133         if(!this.rendered){
36134             this.rendered = true;
36135
36136             this.renderElements(n, a, targetNode, bulkRender);
36137
36138             if(a.qtip){
36139                if(this.textNode.setAttributeNS){
36140                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36141                    if(a.qtipTitle){
36142                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36143                    }
36144                }else{
36145                    this.textNode.setAttribute("ext:qtip", a.qtip);
36146                    if(a.qtipTitle){
36147                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36148                    }
36149                }
36150             }else if(a.qtipCfg){
36151                 a.qtipCfg.target = Roo.id(this.textNode);
36152                 Roo.QuickTips.register(a.qtipCfg);
36153             }
36154             this.initEvents();
36155             if(!this.node.expanded){
36156                 this.updateExpandIcon();
36157             }
36158         }else{
36159             if(bulkRender === true) {
36160                 targetNode.appendChild(this.wrap);
36161             }
36162         }
36163     },
36164
36165     renderElements : function(n, a, targetNode, bulkRender)
36166     {
36167         // add some indent caching, this helps performance when rendering a large tree
36168         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36169         var t = n.getOwnerTree();
36170         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36171         if (typeof(n.attributes.html) != 'undefined') {
36172             txt = n.attributes.html;
36173         }
36174         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36175         var cb = typeof a.checked == 'boolean';
36176         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36177         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36178             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36179             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36180             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36181             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36182             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36183              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36184                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36185             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36186             "</li>"];
36187
36188         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36189             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36190                                 n.nextSibling.ui.getEl(), buf.join(""));
36191         }else{
36192             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36193         }
36194
36195         this.elNode = this.wrap.childNodes[0];
36196         this.ctNode = this.wrap.childNodes[1];
36197         var cs = this.elNode.childNodes;
36198         this.indentNode = cs[0];
36199         this.ecNode = cs[1];
36200         this.iconNode = cs[2];
36201         var index = 3;
36202         if(cb){
36203             this.checkbox = cs[3];
36204             index++;
36205         }
36206         this.anchor = cs[index];
36207         this.textNode = cs[index].firstChild;
36208     },
36209
36210     getAnchor : function(){
36211         return this.anchor;
36212     },
36213
36214     getTextEl : function(){
36215         return this.textNode;
36216     },
36217
36218     getIconEl : function(){
36219         return this.iconNode;
36220     },
36221
36222     isChecked : function(){
36223         return this.checkbox ? this.checkbox.checked : false;
36224     },
36225
36226     updateExpandIcon : function(){
36227         if(this.rendered){
36228             var n = this.node, c1, c2;
36229             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36230             var hasChild = n.hasChildNodes();
36231             if(hasChild){
36232                 if(n.expanded){
36233                     cls += "-minus";
36234                     c1 = "x-tree-node-collapsed";
36235                     c2 = "x-tree-node-expanded";
36236                 }else{
36237                     cls += "-plus";
36238                     c1 = "x-tree-node-expanded";
36239                     c2 = "x-tree-node-collapsed";
36240                 }
36241                 if(this.wasLeaf){
36242                     this.removeClass("x-tree-node-leaf");
36243                     this.wasLeaf = false;
36244                 }
36245                 if(this.c1 != c1 || this.c2 != c2){
36246                     Roo.fly(this.elNode).replaceClass(c1, c2);
36247                     this.c1 = c1; this.c2 = c2;
36248                 }
36249             }else{
36250                 // this changes non-leafs into leafs if they have no children.
36251                 // it's not very rational behaviour..
36252                 
36253                 if(!this.wasLeaf && this.node.leaf){
36254                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36255                     delete this.c1;
36256                     delete this.c2;
36257                     this.wasLeaf = true;
36258                 }
36259             }
36260             var ecc = "x-tree-ec-icon "+cls;
36261             if(this.ecc != ecc){
36262                 this.ecNode.className = ecc;
36263                 this.ecc = ecc;
36264             }
36265         }
36266     },
36267
36268     getChildIndent : function(){
36269         if(!this.childIndent){
36270             var buf = [];
36271             var p = this.node;
36272             while(p){
36273                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36274                     if(!p.isLast()) {
36275                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36276                     } else {
36277                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36278                     }
36279                 }
36280                 p = p.parentNode;
36281             }
36282             this.childIndent = buf.join("");
36283         }
36284         return this.childIndent;
36285     },
36286
36287     renderIndent : function(){
36288         if(this.rendered){
36289             var indent = "";
36290             var p = this.node.parentNode;
36291             if(p){
36292                 indent = p.ui.getChildIndent();
36293             }
36294             if(this.indentMarkup != indent){ // don't rerender if not required
36295                 this.indentNode.innerHTML = indent;
36296                 this.indentMarkup = indent;
36297             }
36298             this.updateExpandIcon();
36299         }
36300     }
36301 };
36302
36303 Roo.tree.RootTreeNodeUI = function(){
36304     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36305 };
36306 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36307     render : function(){
36308         if(!this.rendered){
36309             var targetNode = this.node.ownerTree.innerCt.dom;
36310             this.node.expanded = true;
36311             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36312             this.wrap = this.ctNode = targetNode.firstChild;
36313         }
36314     },
36315     collapse : function(){
36316     },
36317     expand : function(){
36318     }
36319 });/*
36320  * Based on:
36321  * Ext JS Library 1.1.1
36322  * Copyright(c) 2006-2007, Ext JS, LLC.
36323  *
36324  * Originally Released Under LGPL - original licence link has changed is not relivant.
36325  *
36326  * Fork - LGPL
36327  * <script type="text/javascript">
36328  */
36329 /**
36330  * @class Roo.tree.TreeLoader
36331  * @extends Roo.util.Observable
36332  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36333  * nodes from a specified URL. The response must be a javascript Array definition
36334  * who's elements are node definition objects. eg:
36335  * <pre><code>
36336 {  success : true,
36337    data :      [
36338    
36339     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36340     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36341     ]
36342 }
36343
36344
36345 </code></pre>
36346  * <br><br>
36347  * The old style respose with just an array is still supported, but not recommended.
36348  * <br><br>
36349  *
36350  * A server request is sent, and child nodes are loaded only when a node is expanded.
36351  * The loading node's id is passed to the server under the parameter name "node" to
36352  * enable the server to produce the correct child nodes.
36353  * <br><br>
36354  * To pass extra parameters, an event handler may be attached to the "beforeload"
36355  * event, and the parameters specified in the TreeLoader's baseParams property:
36356  * <pre><code>
36357     myTreeLoader.on("beforeload", function(treeLoader, node) {
36358         this.baseParams.category = node.attributes.category;
36359     }, this);
36360     
36361 </code></pre>
36362  *
36363  * This would pass an HTTP parameter called "category" to the server containing
36364  * the value of the Node's "category" attribute.
36365  * @constructor
36366  * Creates a new Treeloader.
36367  * @param {Object} config A config object containing config properties.
36368  */
36369 Roo.tree.TreeLoader = function(config){
36370     this.baseParams = {};
36371     this.requestMethod = "POST";
36372     Roo.apply(this, config);
36373
36374     this.addEvents({
36375     
36376         /**
36377          * @event beforeload
36378          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36379          * @param {Object} This TreeLoader object.
36380          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36381          * @param {Object} callback The callback function specified in the {@link #load} call.
36382          */
36383         beforeload : true,
36384         /**
36385          * @event load
36386          * Fires when the node has been successfuly loaded.
36387          * @param {Object} This TreeLoader object.
36388          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36389          * @param {Object} response The response object containing the data from the server.
36390          */
36391         load : true,
36392         /**
36393          * @event loadexception
36394          * Fires if the network request failed.
36395          * @param {Object} This TreeLoader object.
36396          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36397          * @param {Object} response The response object containing the data from the server.
36398          */
36399         loadexception : true,
36400         /**
36401          * @event create
36402          * Fires before a node is created, enabling you to return custom Node types 
36403          * @param {Object} This TreeLoader object.
36404          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36405          */
36406         create : true
36407     });
36408
36409     Roo.tree.TreeLoader.superclass.constructor.call(this);
36410 };
36411
36412 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36413     /**
36414     * @cfg {String} dataUrl The URL from which to request a Json string which
36415     * specifies an array of node definition object representing the child nodes
36416     * to be loaded.
36417     */
36418     /**
36419     * @cfg {String} requestMethod either GET or POST
36420     * defaults to POST (due to BC)
36421     * to be loaded.
36422     */
36423     /**
36424     * @cfg {Object} baseParams (optional) An object containing properties which
36425     * specify HTTP parameters to be passed to each request for child nodes.
36426     */
36427     /**
36428     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36429     * created by this loader. If the attributes sent by the server have an attribute in this object,
36430     * they take priority.
36431     */
36432     /**
36433     * @cfg {Object} uiProviders (optional) An object containing properties which
36434     * 
36435     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36436     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36437     * <i>uiProvider</i> attribute of a returned child node is a string rather
36438     * than a reference to a TreeNodeUI implementation, this that string value
36439     * is used as a property name in the uiProviders object. You can define the provider named
36440     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36441     */
36442     uiProviders : {},
36443
36444     /**
36445     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36446     * child nodes before loading.
36447     */
36448     clearOnLoad : true,
36449
36450     /**
36451     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36452     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36453     * Grid query { data : [ .....] }
36454     */
36455     
36456     root : false,
36457      /**
36458     * @cfg {String} queryParam (optional) 
36459     * Name of the query as it will be passed on the querystring (defaults to 'node')
36460     * eg. the request will be ?node=[id]
36461     */
36462     
36463     
36464     queryParam: false,
36465     
36466     /**
36467      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36468      * This is called automatically when a node is expanded, but may be used to reload
36469      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36470      * @param {Roo.tree.TreeNode} node
36471      * @param {Function} callback
36472      */
36473     load : function(node, callback){
36474         if(this.clearOnLoad){
36475             while(node.firstChild){
36476                 node.removeChild(node.firstChild);
36477             }
36478         }
36479         if(node.attributes.children){ // preloaded json children
36480             var cs = node.attributes.children;
36481             for(var i = 0, len = cs.length; i < len; i++){
36482                 node.appendChild(this.createNode(cs[i]));
36483             }
36484             if(typeof callback == "function"){
36485                 callback();
36486             }
36487         }else if(this.dataUrl){
36488             this.requestData(node, callback);
36489         }
36490     },
36491
36492     getParams: function(node){
36493         var buf = [], bp = this.baseParams;
36494         for(var key in bp){
36495             if(typeof bp[key] != "function"){
36496                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36497             }
36498         }
36499         var n = this.queryParam === false ? 'node' : this.queryParam;
36500         buf.push(n + "=", encodeURIComponent(node.id));
36501         return buf.join("");
36502     },
36503
36504     requestData : function(node, callback){
36505         if(this.fireEvent("beforeload", this, node, callback) !== false){
36506             this.transId = Roo.Ajax.request({
36507                 method:this.requestMethod,
36508                 url: this.dataUrl||this.url,
36509                 success: this.handleResponse,
36510                 failure: this.handleFailure,
36511                 scope: this,
36512                 argument: {callback: callback, node: node},
36513                 params: this.getParams(node)
36514             });
36515         }else{
36516             // if the load is cancelled, make sure we notify
36517             // the node that we are done
36518             if(typeof callback == "function"){
36519                 callback();
36520             }
36521         }
36522     },
36523
36524     isLoading : function(){
36525         return this.transId ? true : false;
36526     },
36527
36528     abort : function(){
36529         if(this.isLoading()){
36530             Roo.Ajax.abort(this.transId);
36531         }
36532     },
36533
36534     // private
36535     createNode : function(attr)
36536     {
36537         // apply baseAttrs, nice idea Corey!
36538         if(this.baseAttrs){
36539             Roo.applyIf(attr, this.baseAttrs);
36540         }
36541         if(this.applyLoader !== false){
36542             attr.loader = this;
36543         }
36544         // uiProvider = depreciated..
36545         
36546         if(typeof(attr.uiProvider) == 'string'){
36547            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36548                 /**  eval:var:attr */ eval(attr.uiProvider);
36549         }
36550         if(typeof(this.uiProviders['default']) != 'undefined') {
36551             attr.uiProvider = this.uiProviders['default'];
36552         }
36553         
36554         this.fireEvent('create', this, attr);
36555         
36556         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36557         return(attr.leaf ?
36558                         new Roo.tree.TreeNode(attr) :
36559                         new Roo.tree.AsyncTreeNode(attr));
36560     },
36561
36562     processResponse : function(response, node, callback)
36563     {
36564         var json = response.responseText;
36565         try {
36566             
36567             var o = Roo.decode(json);
36568             
36569             if (this.root === false && typeof(o.success) != undefined) {
36570                 this.root = 'data'; // the default behaviour for list like data..
36571                 }
36572                 
36573             if (this.root !== false &&  !o.success) {
36574                 // it's a failure condition.
36575                 var a = response.argument;
36576                 this.fireEvent("loadexception", this, a.node, response);
36577                 Roo.log("Load failed - should have a handler really");
36578                 return;
36579             }
36580             
36581             
36582             
36583             if (this.root !== false) {
36584                  o = o[this.root];
36585             }
36586             
36587             for(var i = 0, len = o.length; i < len; i++){
36588                 var n = this.createNode(o[i]);
36589                 if(n){
36590                     node.appendChild(n);
36591                 }
36592             }
36593             if(typeof callback == "function"){
36594                 callback(this, node);
36595             }
36596         }catch(e){
36597             this.handleFailure(response);
36598         }
36599     },
36600
36601     handleResponse : function(response){
36602         this.transId = false;
36603         var a = response.argument;
36604         this.processResponse(response, a.node, a.callback);
36605         this.fireEvent("load", this, a.node, response);
36606     },
36607
36608     handleFailure : function(response)
36609     {
36610         // should handle failure better..
36611         this.transId = false;
36612         var a = response.argument;
36613         this.fireEvent("loadexception", this, a.node, response);
36614         if(typeof a.callback == "function"){
36615             a.callback(this, a.node);
36616         }
36617     }
36618 });/*
36619  * Based on:
36620  * Ext JS Library 1.1.1
36621  * Copyright(c) 2006-2007, Ext JS, LLC.
36622  *
36623  * Originally Released Under LGPL - original licence link has changed is not relivant.
36624  *
36625  * Fork - LGPL
36626  * <script type="text/javascript">
36627  */
36628
36629 /**
36630 * @class Roo.tree.TreeFilter
36631 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36632 * @param {TreePanel} tree
36633 * @param {Object} config (optional)
36634  */
36635 Roo.tree.TreeFilter = function(tree, config){
36636     this.tree = tree;
36637     this.filtered = {};
36638     Roo.apply(this, config);
36639 };
36640
36641 Roo.tree.TreeFilter.prototype = {
36642     clearBlank:false,
36643     reverse:false,
36644     autoClear:false,
36645     remove:false,
36646
36647      /**
36648      * Filter the data by a specific attribute.
36649      * @param {String/RegExp} value Either string that the attribute value
36650      * should start with or a RegExp to test against the attribute
36651      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36652      * @param {TreeNode} startNode (optional) The node to start the filter at.
36653      */
36654     filter : function(value, attr, startNode){
36655         attr = attr || "text";
36656         var f;
36657         if(typeof value == "string"){
36658             var vlen = value.length;
36659             // auto clear empty filter
36660             if(vlen == 0 && this.clearBlank){
36661                 this.clear();
36662                 return;
36663             }
36664             value = value.toLowerCase();
36665             f = function(n){
36666                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36667             };
36668         }else if(value.exec){ // regex?
36669             f = function(n){
36670                 return value.test(n.attributes[attr]);
36671             };
36672         }else{
36673             throw 'Illegal filter type, must be string or regex';
36674         }
36675         this.filterBy(f, null, startNode);
36676         },
36677
36678     /**
36679      * Filter by a function. The passed function will be called with each
36680      * node in the tree (or from the startNode). If the function returns true, the node is kept
36681      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36682      * @param {Function} fn The filter function
36683      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36684      */
36685     filterBy : function(fn, scope, startNode){
36686         startNode = startNode || this.tree.root;
36687         if(this.autoClear){
36688             this.clear();
36689         }
36690         var af = this.filtered, rv = this.reverse;
36691         var f = function(n){
36692             if(n == startNode){
36693                 return true;
36694             }
36695             if(af[n.id]){
36696                 return false;
36697             }
36698             var m = fn.call(scope || n, n);
36699             if(!m || rv){
36700                 af[n.id] = n;
36701                 n.ui.hide();
36702                 return false;
36703             }
36704             return true;
36705         };
36706         startNode.cascade(f);
36707         if(this.remove){
36708            for(var id in af){
36709                if(typeof id != "function"){
36710                    var n = af[id];
36711                    if(n && n.parentNode){
36712                        n.parentNode.removeChild(n);
36713                    }
36714                }
36715            }
36716         }
36717     },
36718
36719     /**
36720      * Clears the current filter. Note: with the "remove" option
36721      * set a filter cannot be cleared.
36722      */
36723     clear : function(){
36724         var t = this.tree;
36725         var af = this.filtered;
36726         for(var id in af){
36727             if(typeof id != "function"){
36728                 var n = af[id];
36729                 if(n){
36730                     n.ui.show();
36731                 }
36732             }
36733         }
36734         this.filtered = {};
36735     }
36736 };
36737 /*
36738  * Based on:
36739  * Ext JS Library 1.1.1
36740  * Copyright(c) 2006-2007, Ext JS, LLC.
36741  *
36742  * Originally Released Under LGPL - original licence link has changed is not relivant.
36743  *
36744  * Fork - LGPL
36745  * <script type="text/javascript">
36746  */
36747  
36748
36749 /**
36750  * @class Roo.tree.TreeSorter
36751  * Provides sorting of nodes in a TreePanel
36752  * 
36753  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36754  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36755  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36756  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36757  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36758  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36759  * @constructor
36760  * @param {TreePanel} tree
36761  * @param {Object} config
36762  */
36763 Roo.tree.TreeSorter = function(tree, config){
36764     Roo.apply(this, config);
36765     tree.on("beforechildrenrendered", this.doSort, this);
36766     tree.on("append", this.updateSort, this);
36767     tree.on("insert", this.updateSort, this);
36768     
36769     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36770     var p = this.property || "text";
36771     var sortType = this.sortType;
36772     var fs = this.folderSort;
36773     var cs = this.caseSensitive === true;
36774     var leafAttr = this.leafAttr || 'leaf';
36775
36776     this.sortFn = function(n1, n2){
36777         if(fs){
36778             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36779                 return 1;
36780             }
36781             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36782                 return -1;
36783             }
36784         }
36785         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36786         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36787         if(v1 < v2){
36788                         return dsc ? +1 : -1;
36789                 }else if(v1 > v2){
36790                         return dsc ? -1 : +1;
36791         }else{
36792                 return 0;
36793         }
36794     };
36795 };
36796
36797 Roo.tree.TreeSorter.prototype = {
36798     doSort : function(node){
36799         node.sort(this.sortFn);
36800     },
36801     
36802     compareNodes : function(n1, n2){
36803         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36804     },
36805     
36806     updateSort : function(tree, node){
36807         if(node.childrenRendered){
36808             this.doSort.defer(1, this, [node]);
36809         }
36810     }
36811 };/*
36812  * Based on:
36813  * Ext JS Library 1.1.1
36814  * Copyright(c) 2006-2007, Ext JS, LLC.
36815  *
36816  * Originally Released Under LGPL - original licence link has changed is not relivant.
36817  *
36818  * Fork - LGPL
36819  * <script type="text/javascript">
36820  */
36821
36822 if(Roo.dd.DropZone){
36823     
36824 Roo.tree.TreeDropZone = function(tree, config){
36825     this.allowParentInsert = false;
36826     this.allowContainerDrop = false;
36827     this.appendOnly = false;
36828     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36829     this.tree = tree;
36830     this.lastInsertClass = "x-tree-no-status";
36831     this.dragOverData = {};
36832 };
36833
36834 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36835     ddGroup : "TreeDD",
36836     scroll:  true,
36837     
36838     expandDelay : 1000,
36839     
36840     expandNode : function(node){
36841         if(node.hasChildNodes() && !node.isExpanded()){
36842             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36843         }
36844     },
36845     
36846     queueExpand : function(node){
36847         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36848     },
36849     
36850     cancelExpand : function(){
36851         if(this.expandProcId){
36852             clearTimeout(this.expandProcId);
36853             this.expandProcId = false;
36854         }
36855     },
36856     
36857     isValidDropPoint : function(n, pt, dd, e, data){
36858         if(!n || !data){ return false; }
36859         var targetNode = n.node;
36860         var dropNode = data.node;
36861         // default drop rules
36862         if(!(targetNode && targetNode.isTarget && pt)){
36863             return false;
36864         }
36865         if(pt == "append" && targetNode.allowChildren === false){
36866             return false;
36867         }
36868         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36869             return false;
36870         }
36871         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36872             return false;
36873         }
36874         // reuse the object
36875         var overEvent = this.dragOverData;
36876         overEvent.tree = this.tree;
36877         overEvent.target = targetNode;
36878         overEvent.data = data;
36879         overEvent.point = pt;
36880         overEvent.source = dd;
36881         overEvent.rawEvent = e;
36882         overEvent.dropNode = dropNode;
36883         overEvent.cancel = false;  
36884         var result = this.tree.fireEvent("nodedragover", overEvent);
36885         return overEvent.cancel === false && result !== false;
36886     },
36887     
36888     getDropPoint : function(e, n, dd)
36889     {
36890         var tn = n.node;
36891         if(tn.isRoot){
36892             return tn.allowChildren !== false ? "append" : false; // always append for root
36893         }
36894         var dragEl = n.ddel;
36895         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36896         var y = Roo.lib.Event.getPageY(e);
36897         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36898         
36899         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36900         var noAppend = tn.allowChildren === false;
36901         if(this.appendOnly || tn.parentNode.allowChildren === false){
36902             return noAppend ? false : "append";
36903         }
36904         var noBelow = false;
36905         if(!this.allowParentInsert){
36906             noBelow = tn.hasChildNodes() && tn.isExpanded();
36907         }
36908         var q = (b - t) / (noAppend ? 2 : 3);
36909         if(y >= t && y < (t + q)){
36910             return "above";
36911         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36912             return "below";
36913         }else{
36914             return "append";
36915         }
36916     },
36917     
36918     onNodeEnter : function(n, dd, e, data)
36919     {
36920         this.cancelExpand();
36921     },
36922     
36923     onNodeOver : function(n, dd, e, data)
36924     {
36925        
36926         var pt = this.getDropPoint(e, n, dd);
36927         var node = n.node;
36928         
36929         // auto node expand check
36930         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36931             this.queueExpand(node);
36932         }else if(pt != "append"){
36933             this.cancelExpand();
36934         }
36935         
36936         // set the insert point style on the target node
36937         var returnCls = this.dropNotAllowed;
36938         if(this.isValidDropPoint(n, pt, dd, e, data)){
36939            if(pt){
36940                var el = n.ddel;
36941                var cls;
36942                if(pt == "above"){
36943                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36944                    cls = "x-tree-drag-insert-above";
36945                }else if(pt == "below"){
36946                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36947                    cls = "x-tree-drag-insert-below";
36948                }else{
36949                    returnCls = "x-tree-drop-ok-append";
36950                    cls = "x-tree-drag-append";
36951                }
36952                if(this.lastInsertClass != cls){
36953                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36954                    this.lastInsertClass = cls;
36955                }
36956            }
36957        }
36958        return returnCls;
36959     },
36960     
36961     onNodeOut : function(n, dd, e, data){
36962         
36963         this.cancelExpand();
36964         this.removeDropIndicators(n);
36965     },
36966     
36967     onNodeDrop : function(n, dd, e, data){
36968         var point = this.getDropPoint(e, n, dd);
36969         var targetNode = n.node;
36970         targetNode.ui.startDrop();
36971         if(!this.isValidDropPoint(n, point, dd, e, data)){
36972             targetNode.ui.endDrop();
36973             return false;
36974         }
36975         // first try to find the drop node
36976         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36977         var dropEvent = {
36978             tree : this.tree,
36979             target: targetNode,
36980             data: data,
36981             point: point,
36982             source: dd,
36983             rawEvent: e,
36984             dropNode: dropNode,
36985             cancel: !dropNode   
36986         };
36987         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36988         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36989             targetNode.ui.endDrop();
36990             return false;
36991         }
36992         // allow target changing
36993         targetNode = dropEvent.target;
36994         if(point == "append" && !targetNode.isExpanded()){
36995             targetNode.expand(false, null, function(){
36996                 this.completeDrop(dropEvent);
36997             }.createDelegate(this));
36998         }else{
36999             this.completeDrop(dropEvent);
37000         }
37001         return true;
37002     },
37003     
37004     completeDrop : function(de){
37005         var ns = de.dropNode, p = de.point, t = de.target;
37006         if(!(ns instanceof Array)){
37007             ns = [ns];
37008         }
37009         var n;
37010         for(var i = 0, len = ns.length; i < len; i++){
37011             n = ns[i];
37012             if(p == "above"){
37013                 t.parentNode.insertBefore(n, t);
37014             }else if(p == "below"){
37015                 t.parentNode.insertBefore(n, t.nextSibling);
37016             }else{
37017                 t.appendChild(n);
37018             }
37019         }
37020         n.ui.focus();
37021         if(this.tree.hlDrop){
37022             n.ui.highlight();
37023         }
37024         t.ui.endDrop();
37025         this.tree.fireEvent("nodedrop", de);
37026     },
37027     
37028     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37029         if(this.tree.hlDrop){
37030             dropNode.ui.focus();
37031             dropNode.ui.highlight();
37032         }
37033         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37034     },
37035     
37036     getTree : function(){
37037         return this.tree;
37038     },
37039     
37040     removeDropIndicators : function(n){
37041         if(n && n.ddel){
37042             var el = n.ddel;
37043             Roo.fly(el).removeClass([
37044                     "x-tree-drag-insert-above",
37045                     "x-tree-drag-insert-below",
37046                     "x-tree-drag-append"]);
37047             this.lastInsertClass = "_noclass";
37048         }
37049     },
37050     
37051     beforeDragDrop : function(target, e, id){
37052         this.cancelExpand();
37053         return true;
37054     },
37055     
37056     afterRepair : function(data){
37057         if(data && Roo.enableFx){
37058             data.node.ui.highlight();
37059         }
37060         this.hideProxy();
37061     } 
37062     
37063 });
37064
37065 }
37066 /*
37067  * Based on:
37068  * Ext JS Library 1.1.1
37069  * Copyright(c) 2006-2007, Ext JS, LLC.
37070  *
37071  * Originally Released Under LGPL - original licence link has changed is not relivant.
37072  *
37073  * Fork - LGPL
37074  * <script type="text/javascript">
37075  */
37076  
37077
37078 if(Roo.dd.DragZone){
37079 Roo.tree.TreeDragZone = function(tree, config){
37080     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37081     this.tree = tree;
37082 };
37083
37084 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37085     ddGroup : "TreeDD",
37086    
37087     onBeforeDrag : function(data, e){
37088         var n = data.node;
37089         return n && n.draggable && !n.disabled;
37090     },
37091      
37092     
37093     onInitDrag : function(e){
37094         var data = this.dragData;
37095         this.tree.getSelectionModel().select(data.node);
37096         this.proxy.update("");
37097         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37098         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37099     },
37100     
37101     getRepairXY : function(e, data){
37102         return data.node.ui.getDDRepairXY();
37103     },
37104     
37105     onEndDrag : function(data, e){
37106         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37107         
37108         
37109     },
37110     
37111     onValidDrop : function(dd, e, id){
37112         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37113         this.hideProxy();
37114     },
37115     
37116     beforeInvalidDrop : function(e, id){
37117         // this scrolls the original position back into view
37118         var sm = this.tree.getSelectionModel();
37119         sm.clearSelections();
37120         sm.select(this.dragData.node);
37121     }
37122 });
37123 }/*
37124  * Based on:
37125  * Ext JS Library 1.1.1
37126  * Copyright(c) 2006-2007, Ext JS, LLC.
37127  *
37128  * Originally Released Under LGPL - original licence link has changed is not relivant.
37129  *
37130  * Fork - LGPL
37131  * <script type="text/javascript">
37132  */
37133 /**
37134  * @class Roo.tree.TreeEditor
37135  * @extends Roo.Editor
37136  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37137  * as the editor field.
37138  * @constructor
37139  * @param {Object} config (used to be the tree panel.)
37140  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37141  * 
37142  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37143  * @cfg {Roo.form.TextField} field [required] The field configuration
37144  *
37145  * 
37146  */
37147 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37148     var tree = config;
37149     var field;
37150     if (oldconfig) { // old style..
37151         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37152     } else {
37153         // new style..
37154         tree = config.tree;
37155         config.field = config.field  || {};
37156         config.field.xtype = 'TextField';
37157         field = Roo.factory(config.field, Roo.form);
37158     }
37159     config = config || {};
37160     
37161     
37162     this.addEvents({
37163         /**
37164          * @event beforenodeedit
37165          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37166          * false from the handler of this event.
37167          * @param {Editor} this
37168          * @param {Roo.tree.Node} node 
37169          */
37170         "beforenodeedit" : true
37171     });
37172     
37173     //Roo.log(config);
37174     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37175
37176     this.tree = tree;
37177
37178     tree.on('beforeclick', this.beforeNodeClick, this);
37179     tree.getTreeEl().on('mousedown', this.hide, this);
37180     this.on('complete', this.updateNode, this);
37181     this.on('beforestartedit', this.fitToTree, this);
37182     this.on('startedit', this.bindScroll, this, {delay:10});
37183     this.on('specialkey', this.onSpecialKey, this);
37184 };
37185
37186 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37187     /**
37188      * @cfg {String} alignment
37189      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37190      */
37191     alignment: "l-l",
37192     // inherit
37193     autoSize: false,
37194     /**
37195      * @cfg {Boolean} hideEl
37196      * True to hide the bound element while the editor is displayed (defaults to false)
37197      */
37198     hideEl : false,
37199     /**
37200      * @cfg {String} cls
37201      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37202      */
37203     cls: "x-small-editor x-tree-editor",
37204     /**
37205      * @cfg {Boolean} shim
37206      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37207      */
37208     shim:false,
37209     // inherit
37210     shadow:"frame",
37211     /**
37212      * @cfg {Number} maxWidth
37213      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37214      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37215      * scroll and client offsets into account prior to each edit.
37216      */
37217     maxWidth: 250,
37218
37219     editDelay : 350,
37220
37221     // private
37222     fitToTree : function(ed, el){
37223         var td = this.tree.getTreeEl().dom, nd = el.dom;
37224         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37225             td.scrollLeft = nd.offsetLeft;
37226         }
37227         var w = Math.min(
37228                 this.maxWidth,
37229                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37230         this.setSize(w, '');
37231         
37232         return this.fireEvent('beforenodeedit', this, this.editNode);
37233         
37234     },
37235
37236     // private
37237     triggerEdit : function(node){
37238         this.completeEdit();
37239         this.editNode = node;
37240         this.startEdit(node.ui.textNode, node.text);
37241     },
37242
37243     // private
37244     bindScroll : function(){
37245         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37246     },
37247
37248     // private
37249     beforeNodeClick : function(node, e){
37250         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37251         this.lastClick = new Date();
37252         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37253             e.stopEvent();
37254             this.triggerEdit(node);
37255             return false;
37256         }
37257         return true;
37258     },
37259
37260     // private
37261     updateNode : function(ed, value){
37262         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37263         this.editNode.setText(value);
37264     },
37265
37266     // private
37267     onHide : function(){
37268         Roo.tree.TreeEditor.superclass.onHide.call(this);
37269         if(this.editNode){
37270             this.editNode.ui.focus();
37271         }
37272     },
37273
37274     // private
37275     onSpecialKey : function(field, e){
37276         var k = e.getKey();
37277         if(k == e.ESC){
37278             e.stopEvent();
37279             this.cancelEdit();
37280         }else if(k == e.ENTER && !e.hasModifier()){
37281             e.stopEvent();
37282             this.completeEdit();
37283         }
37284     }
37285 });//<Script type="text/javascript">
37286 /*
37287  * Based on:
37288  * Ext JS Library 1.1.1
37289  * Copyright(c) 2006-2007, Ext JS, LLC.
37290  *
37291  * Originally Released Under LGPL - original licence link has changed is not relivant.
37292  *
37293  * Fork - LGPL
37294  * <script type="text/javascript">
37295  */
37296  
37297 /**
37298  * Not documented??? - probably should be...
37299  */
37300
37301 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37302     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37303     
37304     renderElements : function(n, a, targetNode, bulkRender){
37305         //consel.log("renderElements?");
37306         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37307
37308         var t = n.getOwnerTree();
37309         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37310         
37311         var cols = t.columns;
37312         var bw = t.borderWidth;
37313         var c = cols[0];
37314         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37315          var cb = typeof a.checked == "boolean";
37316         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37317         var colcls = 'x-t-' + tid + '-c0';
37318         var buf = [
37319             '<li class="x-tree-node">',
37320             
37321                 
37322                 '<div class="x-tree-node-el ', a.cls,'">',
37323                     // extran...
37324                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37325                 
37326                 
37327                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37328                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37329                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37330                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37331                            (a.iconCls ? ' '+a.iconCls : ''),
37332                            '" unselectable="on" />',
37333                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37334                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37335                              
37336                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37337                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37338                             '<span unselectable="on" qtip="' + tx + '">',
37339                              tx,
37340                              '</span></a>' ,
37341                     '</div>',
37342                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37343                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37344                  ];
37345         for(var i = 1, len = cols.length; i < len; i++){
37346             c = cols[i];
37347             colcls = 'x-t-' + tid + '-c' +i;
37348             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37349             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37350                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37351                       "</div>");
37352          }
37353          
37354          buf.push(
37355             '</a>',
37356             '<div class="x-clear"></div></div>',
37357             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37358             "</li>");
37359         
37360         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37361             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37362                                 n.nextSibling.ui.getEl(), buf.join(""));
37363         }else{
37364             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37365         }
37366         var el = this.wrap.firstChild;
37367         this.elRow = el;
37368         this.elNode = el.firstChild;
37369         this.ranchor = el.childNodes[1];
37370         this.ctNode = this.wrap.childNodes[1];
37371         var cs = el.firstChild.childNodes;
37372         this.indentNode = cs[0];
37373         this.ecNode = cs[1];
37374         this.iconNode = cs[2];
37375         var index = 3;
37376         if(cb){
37377             this.checkbox = cs[3];
37378             index++;
37379         }
37380         this.anchor = cs[index];
37381         
37382         this.textNode = cs[index].firstChild;
37383         
37384         //el.on("click", this.onClick, this);
37385         //el.on("dblclick", this.onDblClick, this);
37386         
37387         
37388        // console.log(this);
37389     },
37390     initEvents : function(){
37391         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37392         
37393             
37394         var a = this.ranchor;
37395
37396         var el = Roo.get(a);
37397
37398         if(Roo.isOpera){ // opera render bug ignores the CSS
37399             el.setStyle("text-decoration", "none");
37400         }
37401
37402         el.on("click", this.onClick, this);
37403         el.on("dblclick", this.onDblClick, this);
37404         el.on("contextmenu", this.onContextMenu, this);
37405         
37406     },
37407     
37408     /*onSelectedChange : function(state){
37409         if(state){
37410             this.focus();
37411             this.addClass("x-tree-selected");
37412         }else{
37413             //this.blur();
37414             this.removeClass("x-tree-selected");
37415         }
37416     },*/
37417     addClass : function(cls){
37418         if(this.elRow){
37419             Roo.fly(this.elRow).addClass(cls);
37420         }
37421         
37422     },
37423     
37424     
37425     removeClass : function(cls){
37426         if(this.elRow){
37427             Roo.fly(this.elRow).removeClass(cls);
37428         }
37429     }
37430
37431     
37432     
37433 });//<Script type="text/javascript">
37434
37435 /*
37436  * Based on:
37437  * Ext JS Library 1.1.1
37438  * Copyright(c) 2006-2007, Ext JS, LLC.
37439  *
37440  * Originally Released Under LGPL - original licence link has changed is not relivant.
37441  *
37442  * Fork - LGPL
37443  * <script type="text/javascript">
37444  */
37445  
37446
37447 /**
37448  * @class Roo.tree.ColumnTree
37449  * @extends Roo.data.TreePanel
37450  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37451  * @cfg {int} borderWidth  compined right/left border allowance
37452  * @constructor
37453  * @param {String/HTMLElement/Element} el The container element
37454  * @param {Object} config
37455  */
37456 Roo.tree.ColumnTree =  function(el, config)
37457 {
37458    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37459    this.addEvents({
37460         /**
37461         * @event resize
37462         * Fire this event on a container when it resizes
37463         * @param {int} w Width
37464         * @param {int} h Height
37465         */
37466        "resize" : true
37467     });
37468     this.on('resize', this.onResize, this);
37469 };
37470
37471 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37472     //lines:false,
37473     
37474     
37475     borderWidth: Roo.isBorderBox ? 0 : 2, 
37476     headEls : false,
37477     
37478     render : function(){
37479         // add the header.....
37480        
37481         Roo.tree.ColumnTree.superclass.render.apply(this);
37482         
37483         this.el.addClass('x-column-tree');
37484         
37485         this.headers = this.el.createChild(
37486             {cls:'x-tree-headers'},this.innerCt.dom);
37487    
37488         var cols = this.columns, c;
37489         var totalWidth = 0;
37490         this.headEls = [];
37491         var  len = cols.length;
37492         for(var i = 0; i < len; i++){
37493              c = cols[i];
37494              totalWidth += c.width;
37495             this.headEls.push(this.headers.createChild({
37496                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37497                  cn: {
37498                      cls:'x-tree-hd-text',
37499                      html: c.header
37500                  },
37501                  style:'width:'+(c.width-this.borderWidth)+'px;'
37502              }));
37503         }
37504         this.headers.createChild({cls:'x-clear'});
37505         // prevent floats from wrapping when clipped
37506         this.headers.setWidth(totalWidth);
37507         //this.innerCt.setWidth(totalWidth);
37508         this.innerCt.setStyle({ overflow: 'auto' });
37509         this.onResize(this.width, this.height);
37510              
37511         
37512     },
37513     onResize : function(w,h)
37514     {
37515         this.height = h;
37516         this.width = w;
37517         // resize cols..
37518         this.innerCt.setWidth(this.width);
37519         this.innerCt.setHeight(this.height-20);
37520         
37521         // headers...
37522         var cols = this.columns, c;
37523         var totalWidth = 0;
37524         var expEl = false;
37525         var len = cols.length;
37526         for(var i = 0; i < len; i++){
37527             c = cols[i];
37528             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37529                 // it's the expander..
37530                 expEl  = this.headEls[i];
37531                 continue;
37532             }
37533             totalWidth += c.width;
37534             
37535         }
37536         if (expEl) {
37537             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37538         }
37539         this.headers.setWidth(w-20);
37540
37541         
37542         
37543         
37544     }
37545 });
37546 /*
37547  * Based on:
37548  * Ext JS Library 1.1.1
37549  * Copyright(c) 2006-2007, Ext JS, LLC.
37550  *
37551  * Originally Released Under LGPL - original licence link has changed is not relivant.
37552  *
37553  * Fork - LGPL
37554  * <script type="text/javascript">
37555  */
37556  
37557 /**
37558  * @class Roo.menu.Menu
37559  * @extends Roo.util.Observable
37560  * @children Roo.menu.BaseItem
37561  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37562  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37563  * @constructor
37564  * Creates a new Menu
37565  * @param {Object} config Configuration options
37566  */
37567 Roo.menu.Menu = function(config){
37568     
37569     Roo.menu.Menu.superclass.constructor.call(this, config);
37570     
37571     this.id = this.id || Roo.id();
37572     this.addEvents({
37573         /**
37574          * @event beforeshow
37575          * Fires before this menu is displayed
37576          * @param {Roo.menu.Menu} this
37577          */
37578         beforeshow : true,
37579         /**
37580          * @event beforehide
37581          * Fires before this menu is hidden
37582          * @param {Roo.menu.Menu} this
37583          */
37584         beforehide : true,
37585         /**
37586          * @event show
37587          * Fires after this menu is displayed
37588          * @param {Roo.menu.Menu} this
37589          */
37590         show : true,
37591         /**
37592          * @event hide
37593          * Fires after this menu is hidden
37594          * @param {Roo.menu.Menu} this
37595          */
37596         hide : true,
37597         /**
37598          * @event click
37599          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37600          * @param {Roo.menu.Menu} this
37601          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37602          * @param {Roo.EventObject} e
37603          */
37604         click : true,
37605         /**
37606          * @event mouseover
37607          * Fires when the mouse is hovering over this menu
37608          * @param {Roo.menu.Menu} this
37609          * @param {Roo.EventObject} e
37610          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37611          */
37612         mouseover : true,
37613         /**
37614          * @event mouseout
37615          * Fires when the mouse exits this menu
37616          * @param {Roo.menu.Menu} this
37617          * @param {Roo.EventObject} e
37618          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37619          */
37620         mouseout : true,
37621         /**
37622          * @event itemclick
37623          * Fires when a menu item contained in this menu is clicked
37624          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37625          * @param {Roo.EventObject} e
37626          */
37627         itemclick: true
37628     });
37629     if (this.registerMenu) {
37630         Roo.menu.MenuMgr.register(this);
37631     }
37632     
37633     var mis = this.items;
37634     this.items = new Roo.util.MixedCollection();
37635     if(mis){
37636         this.add.apply(this, mis);
37637     }
37638 };
37639
37640 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37641     /**
37642      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37643      */
37644     minWidth : 120,
37645     /**
37646      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37647      * for bottom-right shadow (defaults to "sides")
37648      */
37649     shadow : "sides",
37650     /**
37651      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37652      * this menu (defaults to "tl-tr?")
37653      */
37654     subMenuAlign : "tl-tr?",
37655     /**
37656      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37657      * relative to its element of origin (defaults to "tl-bl?")
37658      */
37659     defaultAlign : "tl-bl?",
37660     /**
37661      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37662      */
37663     allowOtherMenus : false,
37664     /**
37665      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37666      */
37667     registerMenu : true,
37668
37669     hidden:true,
37670
37671     // private
37672     render : function(){
37673         if(this.el){
37674             return;
37675         }
37676         var el = this.el = new Roo.Layer({
37677             cls: "x-menu",
37678             shadow:this.shadow,
37679             constrain: false,
37680             parentEl: this.parentEl || document.body,
37681             zindex:15000
37682         });
37683
37684         this.keyNav = new Roo.menu.MenuNav(this);
37685
37686         if(this.plain){
37687             el.addClass("x-menu-plain");
37688         }
37689         if(this.cls){
37690             el.addClass(this.cls);
37691         }
37692         // generic focus element
37693         this.focusEl = el.createChild({
37694             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37695         });
37696         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37697         //disabling touch- as it's causing issues ..
37698         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37699         ul.on('click'   , this.onClick, this);
37700         
37701         
37702         ul.on("mouseover", this.onMouseOver, this);
37703         ul.on("mouseout", this.onMouseOut, this);
37704         this.items.each(function(item){
37705             if (item.hidden) {
37706                 return;
37707             }
37708             
37709             var li = document.createElement("li");
37710             li.className = "x-menu-list-item";
37711             ul.dom.appendChild(li);
37712             item.render(li, this);
37713         }, this);
37714         this.ul = ul;
37715         this.autoWidth();
37716     },
37717
37718     // private
37719     autoWidth : function(){
37720         var el = this.el, ul = this.ul;
37721         if(!el){
37722             return;
37723         }
37724         var w = this.width;
37725         if(w){
37726             el.setWidth(w);
37727         }else if(Roo.isIE){
37728             el.setWidth(this.minWidth);
37729             var t = el.dom.offsetWidth; // force recalc
37730             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37731         }
37732     },
37733
37734     // private
37735     delayAutoWidth : function(){
37736         if(this.rendered){
37737             if(!this.awTask){
37738                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37739             }
37740             this.awTask.delay(20);
37741         }
37742     },
37743
37744     // private
37745     findTargetItem : function(e){
37746         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37747         if(t && t.menuItemId){
37748             return this.items.get(t.menuItemId);
37749         }
37750     },
37751
37752     // private
37753     onClick : function(e){
37754         Roo.log("menu.onClick");
37755         var t = this.findTargetItem(e);
37756         if(!t){
37757             return;
37758         }
37759         Roo.log(e);
37760         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37761             if(t == this.activeItem && t.shouldDeactivate(e)){
37762                 this.activeItem.deactivate();
37763                 delete this.activeItem;
37764                 return;
37765             }
37766             if(t.canActivate){
37767                 this.setActiveItem(t, true);
37768             }
37769             return;
37770             
37771             
37772         }
37773         
37774         t.onClick(e);
37775         this.fireEvent("click", this, t, e);
37776     },
37777
37778     // private
37779     setActiveItem : function(item, autoExpand){
37780         if(item != this.activeItem){
37781             if(this.activeItem){
37782                 this.activeItem.deactivate();
37783             }
37784             this.activeItem = item;
37785             item.activate(autoExpand);
37786         }else if(autoExpand){
37787             item.expandMenu();
37788         }
37789     },
37790
37791     // private
37792     tryActivate : function(start, step){
37793         var items = this.items;
37794         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37795             var item = items.get(i);
37796             if(!item.disabled && item.canActivate){
37797                 this.setActiveItem(item, false);
37798                 return item;
37799             }
37800         }
37801         return false;
37802     },
37803
37804     // private
37805     onMouseOver : function(e){
37806         var t;
37807         if(t = this.findTargetItem(e)){
37808             if(t.canActivate && !t.disabled){
37809                 this.setActiveItem(t, true);
37810             }
37811         }
37812         this.fireEvent("mouseover", this, e, t);
37813     },
37814
37815     // private
37816     onMouseOut : function(e){
37817         var t;
37818         if(t = this.findTargetItem(e)){
37819             if(t == this.activeItem && t.shouldDeactivate(e)){
37820                 this.activeItem.deactivate();
37821                 delete this.activeItem;
37822             }
37823         }
37824         this.fireEvent("mouseout", this, e, t);
37825     },
37826
37827     /**
37828      * Read-only.  Returns true if the menu is currently displayed, else false.
37829      * @type Boolean
37830      */
37831     isVisible : function(){
37832         return this.el && !this.hidden;
37833     },
37834
37835     /**
37836      * Displays this menu relative to another element
37837      * @param {String/HTMLElement/Roo.Element} element The element to align to
37838      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37839      * the element (defaults to this.defaultAlign)
37840      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37841      */
37842     show : function(el, pos, parentMenu){
37843         this.parentMenu = parentMenu;
37844         if(!this.el){
37845             this.render();
37846         }
37847         this.fireEvent("beforeshow", this);
37848         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37849     },
37850
37851     /**
37852      * Displays this menu at a specific xy position
37853      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37854      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37855      */
37856     showAt : function(xy, parentMenu, /* private: */_e){
37857         this.parentMenu = parentMenu;
37858         if(!this.el){
37859             this.render();
37860         }
37861         if(_e !== false){
37862             this.fireEvent("beforeshow", this);
37863             xy = this.el.adjustForConstraints(xy);
37864         }
37865         this.el.setXY(xy);
37866         this.el.show();
37867         this.hidden = false;
37868         this.focus();
37869         this.fireEvent("show", this);
37870     },
37871
37872     focus : function(){
37873         if(!this.hidden){
37874             this.doFocus.defer(50, this);
37875         }
37876     },
37877
37878     doFocus : function(){
37879         if(!this.hidden){
37880             this.focusEl.focus();
37881         }
37882     },
37883
37884     /**
37885      * Hides this menu and optionally all parent menus
37886      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37887      */
37888     hide : function(deep){
37889         if(this.el && this.isVisible()){
37890             this.fireEvent("beforehide", this);
37891             if(this.activeItem){
37892                 this.activeItem.deactivate();
37893                 this.activeItem = null;
37894             }
37895             this.el.hide();
37896             this.hidden = true;
37897             this.fireEvent("hide", this);
37898         }
37899         if(deep === true && this.parentMenu){
37900             this.parentMenu.hide(true);
37901         }
37902     },
37903
37904     /**
37905      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37906      * Any of the following are valid:
37907      * <ul>
37908      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37909      * <li>An HTMLElement object which will be converted to a menu item</li>
37910      * <li>A menu item config object that will be created as a new menu item</li>
37911      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37912      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37913      * </ul>
37914      * Usage:
37915      * <pre><code>
37916 // Create the menu
37917 var menu = new Roo.menu.Menu();
37918
37919 // Create a menu item to add by reference
37920 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37921
37922 // Add a bunch of items at once using different methods.
37923 // Only the last item added will be returned.
37924 var item = menu.add(
37925     menuItem,                // add existing item by ref
37926     'Dynamic Item',          // new TextItem
37927     '-',                     // new separator
37928     { text: 'Config Item' }  // new item by config
37929 );
37930 </code></pre>
37931      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37932      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37933      */
37934     add : function(){
37935         var a = arguments, l = a.length, item;
37936         for(var i = 0; i < l; i++){
37937             var el = a[i];
37938             if ((typeof(el) == "object") && el.xtype && el.xns) {
37939                 el = Roo.factory(el, Roo.menu);
37940             }
37941             
37942             if(el.render){ // some kind of Item
37943                 item = this.addItem(el);
37944             }else if(typeof el == "string"){ // string
37945                 if(el == "separator" || el == "-"){
37946                     item = this.addSeparator();
37947                 }else{
37948                     item = this.addText(el);
37949                 }
37950             }else if(el.tagName || el.el){ // element
37951                 item = this.addElement(el);
37952             }else if(typeof el == "object"){ // must be menu item config?
37953                 item = this.addMenuItem(el);
37954             }
37955         }
37956         return item;
37957     },
37958
37959     /**
37960      * Returns this menu's underlying {@link Roo.Element} object
37961      * @return {Roo.Element} The element
37962      */
37963     getEl : function(){
37964         if(!this.el){
37965             this.render();
37966         }
37967         return this.el;
37968     },
37969
37970     /**
37971      * Adds a separator bar to the menu
37972      * @return {Roo.menu.Item} The menu item that was added
37973      */
37974     addSeparator : function(){
37975         return this.addItem(new Roo.menu.Separator());
37976     },
37977
37978     /**
37979      * Adds an {@link Roo.Element} object to the menu
37980      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37981      * @return {Roo.menu.Item} The menu item that was added
37982      */
37983     addElement : function(el){
37984         return this.addItem(new Roo.menu.BaseItem(el));
37985     },
37986
37987     /**
37988      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37989      * @param {Roo.menu.Item} item The menu item to add
37990      * @return {Roo.menu.Item} The menu item that was added
37991      */
37992     addItem : function(item){
37993         this.items.add(item);
37994         if(this.ul){
37995             var li = document.createElement("li");
37996             li.className = "x-menu-list-item";
37997             this.ul.dom.appendChild(li);
37998             item.render(li, this);
37999             this.delayAutoWidth();
38000         }
38001         return item;
38002     },
38003
38004     /**
38005      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38006      * @param {Object} config A MenuItem config object
38007      * @return {Roo.menu.Item} The menu item that was added
38008      */
38009     addMenuItem : function(config){
38010         if(!(config instanceof Roo.menu.Item)){
38011             if(typeof config.checked == "boolean"){ // must be check menu item config?
38012                 config = new Roo.menu.CheckItem(config);
38013             }else{
38014                 config = new Roo.menu.Item(config);
38015             }
38016         }
38017         return this.addItem(config);
38018     },
38019
38020     /**
38021      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38022      * @param {String} text The text to display in the menu item
38023      * @return {Roo.menu.Item} The menu item that was added
38024      */
38025     addText : function(text){
38026         return this.addItem(new Roo.menu.TextItem({ text : text }));
38027     },
38028
38029     /**
38030      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38031      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38032      * @param {Roo.menu.Item} item The menu item to add
38033      * @return {Roo.menu.Item} The menu item that was added
38034      */
38035     insert : function(index, item){
38036         this.items.insert(index, item);
38037         if(this.ul){
38038             var li = document.createElement("li");
38039             li.className = "x-menu-list-item";
38040             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38041             item.render(li, this);
38042             this.delayAutoWidth();
38043         }
38044         return item;
38045     },
38046
38047     /**
38048      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38049      * @param {Roo.menu.Item} item The menu item to remove
38050      */
38051     remove : function(item){
38052         this.items.removeKey(item.id);
38053         item.destroy();
38054     },
38055
38056     /**
38057      * Removes and destroys all items in the menu
38058      */
38059     removeAll : function(){
38060         var f;
38061         while(f = this.items.first()){
38062             this.remove(f);
38063         }
38064     }
38065 });
38066
38067 // MenuNav is a private utility class used internally by the Menu
38068 Roo.menu.MenuNav = function(menu){
38069     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38070     this.scope = this.menu = menu;
38071 };
38072
38073 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38074     doRelay : function(e, h){
38075         var k = e.getKey();
38076         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38077             this.menu.tryActivate(0, 1);
38078             return false;
38079         }
38080         return h.call(this.scope || this, e, this.menu);
38081     },
38082
38083     up : function(e, m){
38084         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38085             m.tryActivate(m.items.length-1, -1);
38086         }
38087     },
38088
38089     down : function(e, m){
38090         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38091             m.tryActivate(0, 1);
38092         }
38093     },
38094
38095     right : function(e, m){
38096         if(m.activeItem){
38097             m.activeItem.expandMenu(true);
38098         }
38099     },
38100
38101     left : function(e, m){
38102         m.hide();
38103         if(m.parentMenu && m.parentMenu.activeItem){
38104             m.parentMenu.activeItem.activate();
38105         }
38106     },
38107
38108     enter : function(e, m){
38109         if(m.activeItem){
38110             e.stopPropagation();
38111             m.activeItem.onClick(e);
38112             m.fireEvent("click", this, m.activeItem);
38113             return true;
38114         }
38115     }
38116 });/*
38117  * Based on:
38118  * Ext JS Library 1.1.1
38119  * Copyright(c) 2006-2007, Ext JS, LLC.
38120  *
38121  * Originally Released Under LGPL - original licence link has changed is not relivant.
38122  *
38123  * Fork - LGPL
38124  * <script type="text/javascript">
38125  */
38126  
38127 /**
38128  * @class Roo.menu.MenuMgr
38129  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38130  * @singleton
38131  */
38132 Roo.menu.MenuMgr = function(){
38133    var menus, active, groups = {}, attached = false, lastShow = new Date();
38134
38135    // private - called when first menu is created
38136    function init(){
38137        menus = {};
38138        active = new Roo.util.MixedCollection();
38139        Roo.get(document).addKeyListener(27, function(){
38140            if(active.length > 0){
38141                hideAll();
38142            }
38143        });
38144    }
38145
38146    // private
38147    function hideAll(){
38148        if(active && active.length > 0){
38149            var c = active.clone();
38150            c.each(function(m){
38151                m.hide();
38152            });
38153        }
38154    }
38155
38156    // private
38157    function onHide(m){
38158        active.remove(m);
38159        if(active.length < 1){
38160            Roo.get(document).un("mousedown", onMouseDown);
38161            attached = false;
38162        }
38163    }
38164
38165    // private
38166    function onShow(m){
38167        var last = active.last();
38168        lastShow = new Date();
38169        active.add(m);
38170        if(!attached){
38171            Roo.get(document).on("mousedown", onMouseDown);
38172            attached = true;
38173        }
38174        if(m.parentMenu){
38175           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38176           m.parentMenu.activeChild = m;
38177        }else if(last && last.isVisible()){
38178           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38179        }
38180    }
38181
38182    // private
38183    function onBeforeHide(m){
38184        if(m.activeChild){
38185            m.activeChild.hide();
38186        }
38187        if(m.autoHideTimer){
38188            clearTimeout(m.autoHideTimer);
38189            delete m.autoHideTimer;
38190        }
38191    }
38192
38193    // private
38194    function onBeforeShow(m){
38195        var pm = m.parentMenu;
38196        if(!pm && !m.allowOtherMenus){
38197            hideAll();
38198        }else if(pm && pm.activeChild && active != m){
38199            pm.activeChild.hide();
38200        }
38201    }
38202
38203    // private
38204    function onMouseDown(e){
38205        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38206            hideAll();
38207        }
38208    }
38209
38210    // private
38211    function onBeforeCheck(mi, state){
38212        if(state){
38213            var g = groups[mi.group];
38214            for(var i = 0, l = g.length; i < l; i++){
38215                if(g[i] != mi){
38216                    g[i].setChecked(false);
38217                }
38218            }
38219        }
38220    }
38221
38222    return {
38223
38224        /**
38225         * Hides all menus that are currently visible
38226         */
38227        hideAll : function(){
38228             hideAll();  
38229        },
38230
38231        // private
38232        register : function(menu){
38233            if(!menus){
38234                init();
38235            }
38236            menus[menu.id] = menu;
38237            menu.on("beforehide", onBeforeHide);
38238            menu.on("hide", onHide);
38239            menu.on("beforeshow", onBeforeShow);
38240            menu.on("show", onShow);
38241            var g = menu.group;
38242            if(g && menu.events["checkchange"]){
38243                if(!groups[g]){
38244                    groups[g] = [];
38245                }
38246                groups[g].push(menu);
38247                menu.on("checkchange", onCheck);
38248            }
38249        },
38250
38251         /**
38252          * Returns a {@link Roo.menu.Menu} object
38253          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38254          * be used to generate and return a new Menu instance.
38255          */
38256        get : function(menu){
38257            if(typeof menu == "string"){ // menu id
38258                return menus[menu];
38259            }else if(menu.events){  // menu instance
38260                return menu;
38261            }else if(typeof menu.length == 'number'){ // array of menu items?
38262                return new Roo.menu.Menu({items:menu});
38263            }else{ // otherwise, must be a config
38264                return new Roo.menu.Menu(menu);
38265            }
38266        },
38267
38268        // private
38269        unregister : function(menu){
38270            delete menus[menu.id];
38271            menu.un("beforehide", onBeforeHide);
38272            menu.un("hide", onHide);
38273            menu.un("beforeshow", onBeforeShow);
38274            menu.un("show", onShow);
38275            var g = menu.group;
38276            if(g && menu.events["checkchange"]){
38277                groups[g].remove(menu);
38278                menu.un("checkchange", onCheck);
38279            }
38280        },
38281
38282        // private
38283        registerCheckable : function(menuItem){
38284            var g = menuItem.group;
38285            if(g){
38286                if(!groups[g]){
38287                    groups[g] = [];
38288                }
38289                groups[g].push(menuItem);
38290                menuItem.on("beforecheckchange", onBeforeCheck);
38291            }
38292        },
38293
38294        // private
38295        unregisterCheckable : function(menuItem){
38296            var g = menuItem.group;
38297            if(g){
38298                groups[g].remove(menuItem);
38299                menuItem.un("beforecheckchange", onBeforeCheck);
38300            }
38301        }
38302    };
38303 }();/*
38304  * Based on:
38305  * Ext JS Library 1.1.1
38306  * Copyright(c) 2006-2007, Ext JS, LLC.
38307  *
38308  * Originally Released Under LGPL - original licence link has changed is not relivant.
38309  *
38310  * Fork - LGPL
38311  * <script type="text/javascript">
38312  */
38313  
38314
38315 /**
38316  * @class Roo.menu.BaseItem
38317  * @extends Roo.Component
38318  * @abstract
38319  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38320  * management and base configuration options shared by all menu components.
38321  * @constructor
38322  * Creates a new BaseItem
38323  * @param {Object} config Configuration options
38324  */
38325 Roo.menu.BaseItem = function(config){
38326     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38327
38328     this.addEvents({
38329         /**
38330          * @event click
38331          * Fires when this item is clicked
38332          * @param {Roo.menu.BaseItem} this
38333          * @param {Roo.EventObject} e
38334          */
38335         click: true,
38336         /**
38337          * @event activate
38338          * Fires when this item is activated
38339          * @param {Roo.menu.BaseItem} this
38340          */
38341         activate : true,
38342         /**
38343          * @event deactivate
38344          * Fires when this item is deactivated
38345          * @param {Roo.menu.BaseItem} this
38346          */
38347         deactivate : true
38348     });
38349
38350     if(this.handler){
38351         this.on("click", this.handler, this.scope, true);
38352     }
38353 };
38354
38355 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38356     /**
38357      * @cfg {Function} handler
38358      * A function that will handle the click event of this menu item (defaults to undefined)
38359      */
38360     /**
38361      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38362      */
38363     canActivate : false,
38364     
38365      /**
38366      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38367      */
38368     hidden: false,
38369     
38370     /**
38371      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38372      */
38373     activeClass : "x-menu-item-active",
38374     /**
38375      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38376      */
38377     hideOnClick : true,
38378     /**
38379      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38380      */
38381     hideDelay : 100,
38382
38383     // private
38384     ctype: "Roo.menu.BaseItem",
38385
38386     // private
38387     actionMode : "container",
38388
38389     // private
38390     render : function(container, parentMenu){
38391         this.parentMenu = parentMenu;
38392         Roo.menu.BaseItem.superclass.render.call(this, container);
38393         this.container.menuItemId = this.id;
38394     },
38395
38396     // private
38397     onRender : function(container, position){
38398         this.el = Roo.get(this.el);
38399         container.dom.appendChild(this.el.dom);
38400     },
38401
38402     // private
38403     onClick : function(e){
38404         if(!this.disabled && this.fireEvent("click", this, e) !== false
38405                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38406             this.handleClick(e);
38407         }else{
38408             e.stopEvent();
38409         }
38410     },
38411
38412     // private
38413     activate : function(){
38414         if(this.disabled){
38415             return false;
38416         }
38417         var li = this.container;
38418         li.addClass(this.activeClass);
38419         this.region = li.getRegion().adjust(2, 2, -2, -2);
38420         this.fireEvent("activate", this);
38421         return true;
38422     },
38423
38424     // private
38425     deactivate : function(){
38426         this.container.removeClass(this.activeClass);
38427         this.fireEvent("deactivate", this);
38428     },
38429
38430     // private
38431     shouldDeactivate : function(e){
38432         return !this.region || !this.region.contains(e.getPoint());
38433     },
38434
38435     // private
38436     handleClick : function(e){
38437         if(this.hideOnClick){
38438             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38439         }
38440     },
38441
38442     // private
38443     expandMenu : function(autoActivate){
38444         // do nothing
38445     },
38446
38447     // private
38448     hideMenu : function(){
38449         // do nothing
38450     }
38451 });/*
38452  * Based on:
38453  * Ext JS Library 1.1.1
38454  * Copyright(c) 2006-2007, Ext JS, LLC.
38455  *
38456  * Originally Released Under LGPL - original licence link has changed is not relivant.
38457  *
38458  * Fork - LGPL
38459  * <script type="text/javascript">
38460  */
38461  
38462 /**
38463  * @class Roo.menu.Adapter
38464  * @extends Roo.menu.BaseItem
38465  * @abstract
38466  * 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.
38467  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38468  * @constructor
38469  * Creates a new Adapter
38470  * @param {Object} config Configuration options
38471  */
38472 Roo.menu.Adapter = function(component, config){
38473     Roo.menu.Adapter.superclass.constructor.call(this, config);
38474     this.component = component;
38475 };
38476 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38477     // private
38478     canActivate : true,
38479
38480     // private
38481     onRender : function(container, position){
38482         this.component.render(container);
38483         this.el = this.component.getEl();
38484     },
38485
38486     // private
38487     activate : function(){
38488         if(this.disabled){
38489             return false;
38490         }
38491         this.component.focus();
38492         this.fireEvent("activate", this);
38493         return true;
38494     },
38495
38496     // private
38497     deactivate : function(){
38498         this.fireEvent("deactivate", this);
38499     },
38500
38501     // private
38502     disable : function(){
38503         this.component.disable();
38504         Roo.menu.Adapter.superclass.disable.call(this);
38505     },
38506
38507     // private
38508     enable : function(){
38509         this.component.enable();
38510         Roo.menu.Adapter.superclass.enable.call(this);
38511     }
38512 });/*
38513  * Based on:
38514  * Ext JS Library 1.1.1
38515  * Copyright(c) 2006-2007, Ext JS, LLC.
38516  *
38517  * Originally Released Under LGPL - original licence link has changed is not relivant.
38518  *
38519  * Fork - LGPL
38520  * <script type="text/javascript">
38521  */
38522
38523 /**
38524  * @class Roo.menu.TextItem
38525  * @extends Roo.menu.BaseItem
38526  * Adds a static text string to a menu, usually used as either a heading or group separator.
38527  * Note: old style constructor with text is still supported.
38528  * 
38529  * @constructor
38530  * Creates a new TextItem
38531  * @param {Object} cfg Configuration
38532  */
38533 Roo.menu.TextItem = function(cfg){
38534     if (typeof(cfg) == 'string') {
38535         this.text = cfg;
38536     } else {
38537         Roo.apply(this,cfg);
38538     }
38539     
38540     Roo.menu.TextItem.superclass.constructor.call(this);
38541 };
38542
38543 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38544     /**
38545      * @cfg {String} text Text to show on item.
38546      */
38547     text : '',
38548     
38549     /**
38550      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38551      */
38552     hideOnClick : false,
38553     /**
38554      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38555      */
38556     itemCls : "x-menu-text",
38557
38558     // private
38559     onRender : function(){
38560         var s = document.createElement("span");
38561         s.className = this.itemCls;
38562         s.innerHTML = this.text;
38563         this.el = s;
38564         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38565     }
38566 });/*
38567  * Based on:
38568  * Ext JS Library 1.1.1
38569  * Copyright(c) 2006-2007, Ext JS, LLC.
38570  *
38571  * Originally Released Under LGPL - original licence link has changed is not relivant.
38572  *
38573  * Fork - LGPL
38574  * <script type="text/javascript">
38575  */
38576
38577 /**
38578  * @class Roo.menu.Separator
38579  * @extends Roo.menu.BaseItem
38580  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38581  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38582  * @constructor
38583  * @param {Object} config Configuration options
38584  */
38585 Roo.menu.Separator = function(config){
38586     Roo.menu.Separator.superclass.constructor.call(this, config);
38587 };
38588
38589 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38590     /**
38591      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38592      */
38593     itemCls : "x-menu-sep",
38594     /**
38595      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38596      */
38597     hideOnClick : false,
38598
38599     // private
38600     onRender : function(li){
38601         var s = document.createElement("span");
38602         s.className = this.itemCls;
38603         s.innerHTML = "&#160;";
38604         this.el = s;
38605         li.addClass("x-menu-sep-li");
38606         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38607     }
38608 });/*
38609  * Based on:
38610  * Ext JS Library 1.1.1
38611  * Copyright(c) 2006-2007, Ext JS, LLC.
38612  *
38613  * Originally Released Under LGPL - original licence link has changed is not relivant.
38614  *
38615  * Fork - LGPL
38616  * <script type="text/javascript">
38617  */
38618 /**
38619  * @class Roo.menu.Item
38620  * @extends Roo.menu.BaseItem
38621  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38622  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38623  * activation and click handling.
38624  * @constructor
38625  * Creates a new Item
38626  * @param {Object} config Configuration options
38627  */
38628 Roo.menu.Item = function(config){
38629     Roo.menu.Item.superclass.constructor.call(this, config);
38630     if(this.menu){
38631         this.menu = Roo.menu.MenuMgr.get(this.menu);
38632     }
38633 };
38634 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38635     /**
38636      * @cfg {Roo.menu.Menu} menu
38637      * A Sub menu
38638      */
38639     /**
38640      * @cfg {String} text
38641      * The text to show on the menu item.
38642      */
38643     text: '',
38644      /**
38645      * @cfg {String} HTML to render in menu
38646      * The text to show on the menu item (HTML version).
38647      */
38648     html: '',
38649     /**
38650      * @cfg {String} icon
38651      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38652      */
38653     icon: undefined,
38654     /**
38655      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38656      */
38657     itemCls : "x-menu-item",
38658     /**
38659      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38660      */
38661     canActivate : true,
38662     /**
38663      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38664      */
38665     showDelay: 200,
38666     // doc'd in BaseItem
38667     hideDelay: 200,
38668
38669     // private
38670     ctype: "Roo.menu.Item",
38671     
38672     // private
38673     onRender : function(container, position){
38674         var el = document.createElement("a");
38675         el.hideFocus = true;
38676         el.unselectable = "on";
38677         el.href = this.href || "#";
38678         if(this.hrefTarget){
38679             el.target = this.hrefTarget;
38680         }
38681         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38682         
38683         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38684         
38685         el.innerHTML = String.format(
38686                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38687                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38688         this.el = el;
38689         Roo.menu.Item.superclass.onRender.call(this, container, position);
38690     },
38691
38692     /**
38693      * Sets the text to display in this menu item
38694      * @param {String} text The text to display
38695      * @param {Boolean} isHTML true to indicate text is pure html.
38696      */
38697     setText : function(text, isHTML){
38698         if (isHTML) {
38699             this.html = text;
38700         } else {
38701             this.text = text;
38702             this.html = '';
38703         }
38704         if(this.rendered){
38705             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38706      
38707             this.el.update(String.format(
38708                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38709                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38710             this.parentMenu.autoWidth();
38711         }
38712     },
38713
38714     // private
38715     handleClick : function(e){
38716         if(!this.href){ // if no link defined, stop the event automatically
38717             e.stopEvent();
38718         }
38719         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38720     },
38721
38722     // private
38723     activate : function(autoExpand){
38724         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38725             this.focus();
38726             if(autoExpand){
38727                 this.expandMenu();
38728             }
38729         }
38730         return true;
38731     },
38732
38733     // private
38734     shouldDeactivate : function(e){
38735         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38736             if(this.menu && this.menu.isVisible()){
38737                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38738             }
38739             return true;
38740         }
38741         return false;
38742     },
38743
38744     // private
38745     deactivate : function(){
38746         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38747         this.hideMenu();
38748     },
38749
38750     // private
38751     expandMenu : function(autoActivate){
38752         if(!this.disabled && this.menu){
38753             clearTimeout(this.hideTimer);
38754             delete this.hideTimer;
38755             if(!this.menu.isVisible() && !this.showTimer){
38756                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38757             }else if (this.menu.isVisible() && autoActivate){
38758                 this.menu.tryActivate(0, 1);
38759             }
38760         }
38761     },
38762
38763     // private
38764     deferExpand : function(autoActivate){
38765         delete this.showTimer;
38766         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38767         if(autoActivate){
38768             this.menu.tryActivate(0, 1);
38769         }
38770     },
38771
38772     // private
38773     hideMenu : function(){
38774         clearTimeout(this.showTimer);
38775         delete this.showTimer;
38776         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38777             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38778         }
38779     },
38780
38781     // private
38782     deferHide : function(){
38783         delete this.hideTimer;
38784         this.menu.hide();
38785     }
38786 });/*
38787  * Based on:
38788  * Ext JS Library 1.1.1
38789  * Copyright(c) 2006-2007, Ext JS, LLC.
38790  *
38791  * Originally Released Under LGPL - original licence link has changed is not relivant.
38792  *
38793  * Fork - LGPL
38794  * <script type="text/javascript">
38795  */
38796  
38797 /**
38798  * @class Roo.menu.CheckItem
38799  * @extends Roo.menu.Item
38800  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38801  * @constructor
38802  * Creates a new CheckItem
38803  * @param {Object} config Configuration options
38804  */
38805 Roo.menu.CheckItem = function(config){
38806     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38807     this.addEvents({
38808         /**
38809          * @event beforecheckchange
38810          * Fires before the checked value is set, providing an opportunity to cancel if needed
38811          * @param {Roo.menu.CheckItem} this
38812          * @param {Boolean} checked The new checked value that will be set
38813          */
38814         "beforecheckchange" : true,
38815         /**
38816          * @event checkchange
38817          * Fires after the checked value has been set
38818          * @param {Roo.menu.CheckItem} this
38819          * @param {Boolean} checked The checked value that was set
38820          */
38821         "checkchange" : true
38822     });
38823     if(this.checkHandler){
38824         this.on('checkchange', this.checkHandler, this.scope);
38825     }
38826 };
38827 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38828     /**
38829      * @cfg {String} group
38830      * All check items with the same group name will automatically be grouped into a single-select
38831      * radio button group (defaults to '')
38832      */
38833     /**
38834      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38835      */
38836     itemCls : "x-menu-item x-menu-check-item",
38837     /**
38838      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38839      */
38840     groupClass : "x-menu-group-item",
38841
38842     /**
38843      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38844      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38845      * initialized with checked = true will be rendered as checked.
38846      */
38847     checked: false,
38848
38849     // private
38850     ctype: "Roo.menu.CheckItem",
38851
38852     // private
38853     onRender : function(c){
38854         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38855         if(this.group){
38856             this.el.addClass(this.groupClass);
38857         }
38858         Roo.menu.MenuMgr.registerCheckable(this);
38859         if(this.checked){
38860             this.checked = false;
38861             this.setChecked(true, true);
38862         }
38863     },
38864
38865     // private
38866     destroy : function(){
38867         if(this.rendered){
38868             Roo.menu.MenuMgr.unregisterCheckable(this);
38869         }
38870         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38871     },
38872
38873     /**
38874      * Set the checked state of this item
38875      * @param {Boolean} checked The new checked value
38876      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38877      */
38878     setChecked : function(state, suppressEvent){
38879         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38880             if(this.container){
38881                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38882             }
38883             this.checked = state;
38884             if(suppressEvent !== true){
38885                 this.fireEvent("checkchange", this, state);
38886             }
38887         }
38888     },
38889
38890     // private
38891     handleClick : function(e){
38892        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38893            this.setChecked(!this.checked);
38894        }
38895        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38896     }
38897 });/*
38898  * Based on:
38899  * Ext JS Library 1.1.1
38900  * Copyright(c) 2006-2007, Ext JS, LLC.
38901  *
38902  * Originally Released Under LGPL - original licence link has changed is not relivant.
38903  *
38904  * Fork - LGPL
38905  * <script type="text/javascript">
38906  */
38907  
38908 /**
38909  * @class Roo.menu.DateItem
38910  * @extends Roo.menu.Adapter
38911  * A menu item that wraps the {@link Roo.DatPicker} component.
38912  * @constructor
38913  * Creates a new DateItem
38914  * @param {Object} config Configuration options
38915  */
38916 Roo.menu.DateItem = function(config){
38917     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38918     /** The Roo.DatePicker object @type Roo.DatePicker */
38919     this.picker = this.component;
38920     this.addEvents({select: true});
38921     
38922     this.picker.on("render", function(picker){
38923         picker.getEl().swallowEvent("click");
38924         picker.container.addClass("x-menu-date-item");
38925     });
38926
38927     this.picker.on("select", this.onSelect, this);
38928 };
38929
38930 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38931     // private
38932     onSelect : function(picker, date){
38933         this.fireEvent("select", this, date, picker);
38934         Roo.menu.DateItem.superclass.handleClick.call(this);
38935     }
38936 });/*
38937  * Based on:
38938  * Ext JS Library 1.1.1
38939  * Copyright(c) 2006-2007, Ext JS, LLC.
38940  *
38941  * Originally Released Under LGPL - original licence link has changed is not relivant.
38942  *
38943  * Fork - LGPL
38944  * <script type="text/javascript">
38945  */
38946  
38947 /**
38948  * @class Roo.menu.ColorItem
38949  * @extends Roo.menu.Adapter
38950  * A menu item that wraps the {@link Roo.ColorPalette} component.
38951  * @constructor
38952  * Creates a new ColorItem
38953  * @param {Object} config Configuration options
38954  */
38955 Roo.menu.ColorItem = function(config){
38956     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38957     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38958     this.palette = this.component;
38959     this.relayEvents(this.palette, ["select"]);
38960     if(this.selectHandler){
38961         this.on('select', this.selectHandler, this.scope);
38962     }
38963 };
38964 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38965  * Based on:
38966  * Ext JS Library 1.1.1
38967  * Copyright(c) 2006-2007, Ext JS, LLC.
38968  *
38969  * Originally Released Under LGPL - original licence link has changed is not relivant.
38970  *
38971  * Fork - LGPL
38972  * <script type="text/javascript">
38973  */
38974  
38975
38976 /**
38977  * @class Roo.menu.DateMenu
38978  * @extends Roo.menu.Menu
38979  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38980  * @constructor
38981  * Creates a new DateMenu
38982  * @param {Object} config Configuration options
38983  */
38984 Roo.menu.DateMenu = function(config){
38985     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38986     this.plain = true;
38987     var di = new Roo.menu.DateItem(config);
38988     this.add(di);
38989     /**
38990      * The {@link Roo.DatePicker} instance for this DateMenu
38991      * @type DatePicker
38992      */
38993     this.picker = di.picker;
38994     /**
38995      * @event select
38996      * @param {DatePicker} picker
38997      * @param {Date} date
38998      */
38999     this.relayEvents(di, ["select"]);
39000     this.on('beforeshow', function(){
39001         if(this.picker){
39002             this.picker.hideMonthPicker(false);
39003         }
39004     }, this);
39005 };
39006 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39007     cls:'x-date-menu'
39008 });/*
39009  * Based on:
39010  * Ext JS Library 1.1.1
39011  * Copyright(c) 2006-2007, Ext JS, LLC.
39012  *
39013  * Originally Released Under LGPL - original licence link has changed is not relivant.
39014  *
39015  * Fork - LGPL
39016  * <script type="text/javascript">
39017  */
39018  
39019
39020 /**
39021  * @class Roo.menu.ColorMenu
39022  * @extends Roo.menu.Menu
39023  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39024  * @constructor
39025  * Creates a new ColorMenu
39026  * @param {Object} config Configuration options
39027  */
39028 Roo.menu.ColorMenu = function(config){
39029     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39030     this.plain = true;
39031     var ci = new Roo.menu.ColorItem(config);
39032     this.add(ci);
39033     /**
39034      * The {@link Roo.ColorPalette} instance for this ColorMenu
39035      * @type ColorPalette
39036      */
39037     this.palette = ci.palette;
39038     /**
39039      * @event select
39040      * @param {ColorPalette} palette
39041      * @param {String} color
39042      */
39043     this.relayEvents(ci, ["select"]);
39044 };
39045 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39046  * Based on:
39047  * Ext JS Library 1.1.1
39048  * Copyright(c) 2006-2007, Ext JS, LLC.
39049  *
39050  * Originally Released Under LGPL - original licence link has changed is not relivant.
39051  *
39052  * Fork - LGPL
39053  * <script type="text/javascript">
39054  */
39055  
39056 /**
39057  * @class Roo.form.TextItem
39058  * @extends Roo.BoxComponent
39059  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39060  * @constructor
39061  * Creates a new TextItem
39062  * @param {Object} config Configuration options
39063  */
39064 Roo.form.TextItem = function(config){
39065     Roo.form.TextItem.superclass.constructor.call(this, config);
39066 };
39067
39068 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39069     
39070     /**
39071      * @cfg {String} tag the tag for this item (default div)
39072      */
39073     tag : 'div',
39074     /**
39075      * @cfg {String} html the content for this item
39076      */
39077     html : '',
39078     
39079     getAutoCreate : function()
39080     {
39081         var cfg = {
39082             id: this.id,
39083             tag: this.tag,
39084             html: this.html,
39085             cls: 'x-form-item'
39086         };
39087         
39088         return cfg;
39089         
39090     },
39091     
39092     onRender : function(ct, position)
39093     {
39094         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39095         
39096         if(!this.el){
39097             var cfg = this.getAutoCreate();
39098             if(!cfg.name){
39099                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39100             }
39101             if (!cfg.name.length) {
39102                 delete cfg.name;
39103             }
39104             this.el = ct.createChild(cfg, position);
39105         }
39106     },
39107     /*
39108      * setHTML
39109      * @param {String} html update the Contents of the element.
39110      */
39111     setHTML : function(html)
39112     {
39113         this.fieldEl.dom.innerHTML = html;
39114     }
39115     
39116 });/*
39117  * Based on:
39118  * Ext JS Library 1.1.1
39119  * Copyright(c) 2006-2007, Ext JS, LLC.
39120  *
39121  * Originally Released Under LGPL - original licence link has changed is not relivant.
39122  *
39123  * Fork - LGPL
39124  * <script type="text/javascript">
39125  */
39126  
39127 /**
39128  * @class Roo.form.Field
39129  * @extends Roo.BoxComponent
39130  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39131  * @constructor
39132  * Creates a new Field
39133  * @param {Object} config Configuration options
39134  */
39135 Roo.form.Field = function(config){
39136     Roo.form.Field.superclass.constructor.call(this, config);
39137 };
39138
39139 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39140     /**
39141      * @cfg {String} fieldLabel Label to use when rendering a form.
39142      */
39143        /**
39144      * @cfg {String} qtip Mouse over tip
39145      */
39146      
39147     /**
39148      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39149      */
39150     invalidClass : "x-form-invalid",
39151     /**
39152      * @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")
39153      */
39154     invalidText : "The value in this field is invalid",
39155     /**
39156      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39157      */
39158     focusClass : "x-form-focus",
39159     /**
39160      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39161       automatic validation (defaults to "keyup").
39162      */
39163     validationEvent : "keyup",
39164     /**
39165      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39166      */
39167     validateOnBlur : true,
39168     /**
39169      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39170      */
39171     validationDelay : 250,
39172     /**
39173      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39174      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39175      */
39176     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39177     /**
39178      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39179      */
39180     fieldClass : "x-form-field",
39181     /**
39182      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39183      *<pre>
39184 Value         Description
39185 -----------   ----------------------------------------------------------------------
39186 qtip          Display a quick tip when the user hovers over the field
39187 title         Display a default browser title attribute popup
39188 under         Add a block div beneath the field containing the error text
39189 side          Add an error icon to the right of the field with a popup on hover
39190 [element id]  Add the error text directly to the innerHTML of the specified element
39191 </pre>
39192      */
39193     msgTarget : 'qtip',
39194     /**
39195      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39196      */
39197     msgFx : 'normal',
39198
39199     /**
39200      * @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.
39201      */
39202     readOnly : false,
39203
39204     /**
39205      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39206      */
39207     disabled : false,
39208
39209     /**
39210      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39211      */
39212     inputType : undefined,
39213     
39214     /**
39215      * @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).
39216          */
39217         tabIndex : undefined,
39218         
39219     // private
39220     isFormField : true,
39221
39222     // private
39223     hasFocus : false,
39224     /**
39225      * @property {Roo.Element} fieldEl
39226      * Element Containing the rendered Field (with label etc.)
39227      */
39228     /**
39229      * @cfg {Mixed} value A value to initialize this field with.
39230      */
39231     value : undefined,
39232
39233     /**
39234      * @cfg {String} name The field's HTML name attribute.
39235      */
39236     /**
39237      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39238      */
39239     // private
39240     loadedValue : false,
39241      
39242      
39243         // private ??
39244         initComponent : function(){
39245         Roo.form.Field.superclass.initComponent.call(this);
39246         this.addEvents({
39247             /**
39248              * @event focus
39249              * Fires when this field receives input focus.
39250              * @param {Roo.form.Field} this
39251              */
39252             focus : true,
39253             /**
39254              * @event blur
39255              * Fires when this field loses input focus.
39256              * @param {Roo.form.Field} this
39257              */
39258             blur : true,
39259             /**
39260              * @event specialkey
39261              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39262              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39263              * @param {Roo.form.Field} this
39264              * @param {Roo.EventObject} e The event object
39265              */
39266             specialkey : true,
39267             /**
39268              * @event change
39269              * Fires just before the field blurs if the field value has changed.
39270              * @param {Roo.form.Field} this
39271              * @param {Mixed} newValue The new value
39272              * @param {Mixed} oldValue The original value
39273              */
39274             change : true,
39275             /**
39276              * @event invalid
39277              * Fires after the field has been marked as invalid.
39278              * @param {Roo.form.Field} this
39279              * @param {String} msg The validation message
39280              */
39281             invalid : true,
39282             /**
39283              * @event valid
39284              * Fires after the field has been validated with no errors.
39285              * @param {Roo.form.Field} this
39286              */
39287             valid : true,
39288              /**
39289              * @event keyup
39290              * Fires after the key up
39291              * @param {Roo.form.Field} this
39292              * @param {Roo.EventObject}  e The event Object
39293              */
39294             keyup : true
39295         });
39296     },
39297
39298     /**
39299      * Returns the name attribute of the field if available
39300      * @return {String} name The field name
39301      */
39302     getName: function(){
39303          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39304     },
39305
39306     // private
39307     onRender : function(ct, position){
39308         Roo.form.Field.superclass.onRender.call(this, ct, position);
39309         if(!this.el){
39310             var cfg = this.getAutoCreate();
39311             if(!cfg.name){
39312                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39313             }
39314             if (!cfg.name.length) {
39315                 delete cfg.name;
39316             }
39317             if(this.inputType){
39318                 cfg.type = this.inputType;
39319             }
39320             this.el = ct.createChild(cfg, position);
39321         }
39322         var type = this.el.dom.type;
39323         if(type){
39324             if(type == 'password'){
39325                 type = 'text';
39326             }
39327             this.el.addClass('x-form-'+type);
39328         }
39329         if(this.readOnly){
39330             this.el.dom.readOnly = true;
39331         }
39332         if(this.tabIndex !== undefined){
39333             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39334         }
39335
39336         this.el.addClass([this.fieldClass, this.cls]);
39337         this.initValue();
39338     },
39339
39340     /**
39341      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39342      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39343      * @return {Roo.form.Field} this
39344      */
39345     applyTo : function(target){
39346         this.allowDomMove = false;
39347         this.el = Roo.get(target);
39348         this.render(this.el.dom.parentNode);
39349         return this;
39350     },
39351
39352     // private
39353     initValue : function(){
39354         if(this.value !== undefined){
39355             this.setValue(this.value);
39356         }else if(this.el.dom.value.length > 0){
39357             this.setValue(this.el.dom.value);
39358         }
39359     },
39360
39361     /**
39362      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39363      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39364      */
39365     isDirty : function() {
39366         if(this.disabled) {
39367             return false;
39368         }
39369         return String(this.getValue()) !== String(this.originalValue);
39370     },
39371
39372     /**
39373      * stores the current value in loadedValue
39374      */
39375     resetHasChanged : function()
39376     {
39377         this.loadedValue = String(this.getValue());
39378     },
39379     /**
39380      * checks the current value against the 'loaded' value.
39381      * Note - will return false if 'resetHasChanged' has not been called first.
39382      */
39383     hasChanged : function()
39384     {
39385         if(this.disabled || this.readOnly) {
39386             return false;
39387         }
39388         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39389     },
39390     
39391     
39392     
39393     // private
39394     afterRender : function(){
39395         Roo.form.Field.superclass.afterRender.call(this);
39396         this.initEvents();
39397     },
39398
39399     // private
39400     fireKey : function(e){
39401         //Roo.log('field ' + e.getKey());
39402         if(e.isNavKeyPress()){
39403             this.fireEvent("specialkey", this, e);
39404         }
39405     },
39406
39407     /**
39408      * Resets the current field value to the originally loaded value and clears any validation messages
39409      */
39410     reset : function(){
39411         this.setValue(this.resetValue);
39412         this.originalValue = this.getValue();
39413         this.clearInvalid();
39414     },
39415
39416     // private
39417     initEvents : function(){
39418         // safari killled keypress - so keydown is now used..
39419         this.el.on("keydown" , this.fireKey,  this);
39420         this.el.on("focus", this.onFocus,  this);
39421         this.el.on("blur", this.onBlur,  this);
39422         this.el.relayEvent('keyup', this);
39423
39424         // reference to original value for reset
39425         this.originalValue = this.getValue();
39426         this.resetValue =  this.getValue();
39427     },
39428
39429     // private
39430     onFocus : function(){
39431         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39432             this.el.addClass(this.focusClass);
39433         }
39434         if(!this.hasFocus){
39435             this.hasFocus = true;
39436             this.startValue = this.getValue();
39437             this.fireEvent("focus", this);
39438         }
39439     },
39440
39441     beforeBlur : Roo.emptyFn,
39442
39443     // private
39444     onBlur : function(){
39445         this.beforeBlur();
39446         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39447             this.el.removeClass(this.focusClass);
39448         }
39449         this.hasFocus = false;
39450         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39451             this.validate();
39452         }
39453         var v = this.getValue();
39454         if(String(v) !== String(this.startValue)){
39455             this.fireEvent('change', this, v, this.startValue);
39456         }
39457         this.fireEvent("blur", this);
39458     },
39459
39460     /**
39461      * Returns whether or not the field value is currently valid
39462      * @param {Boolean} preventMark True to disable marking the field invalid
39463      * @return {Boolean} True if the value is valid, else false
39464      */
39465     isValid : function(preventMark){
39466         if(this.disabled){
39467             return true;
39468         }
39469         var restore = this.preventMark;
39470         this.preventMark = preventMark === true;
39471         var v = this.validateValue(this.processValue(this.getRawValue()));
39472         this.preventMark = restore;
39473         return v;
39474     },
39475
39476     /**
39477      * Validates the field value
39478      * @return {Boolean} True if the value is valid, else false
39479      */
39480     validate : function(){
39481         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39482             this.clearInvalid();
39483             return true;
39484         }
39485         return false;
39486     },
39487
39488     processValue : function(value){
39489         return value;
39490     },
39491
39492     // private
39493     // Subclasses should provide the validation implementation by overriding this
39494     validateValue : function(value){
39495         return true;
39496     },
39497
39498     /**
39499      * Mark this field as invalid
39500      * @param {String} msg The validation message
39501      */
39502     markInvalid : function(msg){
39503         if(!this.rendered || this.preventMark){ // not rendered
39504             return;
39505         }
39506         
39507         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39508         
39509         obj.el.addClass(this.invalidClass);
39510         msg = msg || this.invalidText;
39511         switch(this.msgTarget){
39512             case 'qtip':
39513                 obj.el.dom.qtip = msg;
39514                 obj.el.dom.qclass = 'x-form-invalid-tip';
39515                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39516                     Roo.QuickTips.enable();
39517                 }
39518                 break;
39519             case 'title':
39520                 this.el.dom.title = msg;
39521                 break;
39522             case 'under':
39523                 if(!this.errorEl){
39524                     var elp = this.el.findParent('.x-form-element', 5, true);
39525                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39526                     this.errorEl.setWidth(elp.getWidth(true)-20);
39527                 }
39528                 this.errorEl.update(msg);
39529                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39530                 break;
39531             case 'side':
39532                 if(!this.errorIcon){
39533                     var elp = this.el.findParent('.x-form-element', 5, true);
39534                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39535                 }
39536                 this.alignErrorIcon();
39537                 this.errorIcon.dom.qtip = msg;
39538                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39539                 this.errorIcon.show();
39540                 this.on('resize', this.alignErrorIcon, this);
39541                 break;
39542             default:
39543                 var t = Roo.getDom(this.msgTarget);
39544                 t.innerHTML = msg;
39545                 t.style.display = this.msgDisplay;
39546                 break;
39547         }
39548         this.fireEvent('invalid', this, msg);
39549     },
39550
39551     // private
39552     alignErrorIcon : function(){
39553         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39554     },
39555
39556     /**
39557      * Clear any invalid styles/messages for this field
39558      */
39559     clearInvalid : function(){
39560         if(!this.rendered || this.preventMark){ // not rendered
39561             return;
39562         }
39563         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39564         
39565         obj.el.removeClass(this.invalidClass);
39566         switch(this.msgTarget){
39567             case 'qtip':
39568                 obj.el.dom.qtip = '';
39569                 break;
39570             case 'title':
39571                 this.el.dom.title = '';
39572                 break;
39573             case 'under':
39574                 if(this.errorEl){
39575                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39576                 }
39577                 break;
39578             case 'side':
39579                 if(this.errorIcon){
39580                     this.errorIcon.dom.qtip = '';
39581                     this.errorIcon.hide();
39582                     this.un('resize', this.alignErrorIcon, this);
39583                 }
39584                 break;
39585             default:
39586                 var t = Roo.getDom(this.msgTarget);
39587                 t.innerHTML = '';
39588                 t.style.display = 'none';
39589                 break;
39590         }
39591         this.fireEvent('valid', this);
39592     },
39593
39594     /**
39595      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39596      * @return {Mixed} value The field value
39597      */
39598     getRawValue : function(){
39599         var v = this.el.getValue();
39600         
39601         return v;
39602     },
39603
39604     /**
39605      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39606      * @return {Mixed} value The field value
39607      */
39608     getValue : function(){
39609         var v = this.el.getValue();
39610          
39611         return v;
39612     },
39613
39614     /**
39615      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39616      * @param {Mixed} value The value to set
39617      */
39618     setRawValue : function(v){
39619         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39620     },
39621
39622     /**
39623      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39624      * @param {Mixed} value The value to set
39625      */
39626     setValue : function(v){
39627         this.value = v;
39628         if(this.rendered){
39629             this.el.dom.value = (v === null || v === undefined ? '' : v);
39630              this.validate();
39631         }
39632     },
39633
39634     adjustSize : function(w, h){
39635         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39636         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39637         return s;
39638     },
39639
39640     adjustWidth : function(tag, w){
39641         tag = tag.toLowerCase();
39642         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39643             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39644                 if(tag == 'input'){
39645                     return w + 2;
39646                 }
39647                 if(tag == 'textarea'){
39648                     return w-2;
39649                 }
39650             }else if(Roo.isOpera){
39651                 if(tag == 'input'){
39652                     return w + 2;
39653                 }
39654                 if(tag == 'textarea'){
39655                     return w-2;
39656                 }
39657             }
39658         }
39659         return w;
39660     }
39661 });
39662
39663
39664 // anything other than normal should be considered experimental
39665 Roo.form.Field.msgFx = {
39666     normal : {
39667         show: function(msgEl, f){
39668             msgEl.setDisplayed('block');
39669         },
39670
39671         hide : function(msgEl, f){
39672             msgEl.setDisplayed(false).update('');
39673         }
39674     },
39675
39676     slide : {
39677         show: function(msgEl, f){
39678             msgEl.slideIn('t', {stopFx:true});
39679         },
39680
39681         hide : function(msgEl, f){
39682             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39683         }
39684     },
39685
39686     slideRight : {
39687         show: function(msgEl, f){
39688             msgEl.fixDisplay();
39689             msgEl.alignTo(f.el, 'tl-tr');
39690             msgEl.slideIn('l', {stopFx:true});
39691         },
39692
39693         hide : function(msgEl, f){
39694             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39695         }
39696     }
39697 };/*
39698  * Based on:
39699  * Ext JS Library 1.1.1
39700  * Copyright(c) 2006-2007, Ext JS, LLC.
39701  *
39702  * Originally Released Under LGPL - original licence link has changed is not relivant.
39703  *
39704  * Fork - LGPL
39705  * <script type="text/javascript">
39706  */
39707  
39708
39709 /**
39710  * @class Roo.form.TextField
39711  * @extends Roo.form.Field
39712  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39713  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39714  * @constructor
39715  * Creates a new TextField
39716  * @param {Object} config Configuration options
39717  */
39718 Roo.form.TextField = function(config){
39719     Roo.form.TextField.superclass.constructor.call(this, config);
39720     this.addEvents({
39721         /**
39722          * @event autosize
39723          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39724          * according to the default logic, but this event provides a hook for the developer to apply additional
39725          * logic at runtime to resize the field if needed.
39726              * @param {Roo.form.Field} this This text field
39727              * @param {Number} width The new field width
39728              */
39729         autosize : true
39730     });
39731 };
39732
39733 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39734     /**
39735      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39736      */
39737     grow : false,
39738     /**
39739      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39740      */
39741     growMin : 30,
39742     /**
39743      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39744      */
39745     growMax : 800,
39746     /**
39747      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39748      */
39749     vtype : null,
39750     /**
39751      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39752      */
39753     maskRe : null,
39754     /**
39755      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39756      */
39757     disableKeyFilter : false,
39758     /**
39759      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39760      */
39761     allowBlank : true,
39762     /**
39763      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39764      */
39765     minLength : 0,
39766     /**
39767      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39768      */
39769     maxLength : Number.MAX_VALUE,
39770     /**
39771      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39772      */
39773     minLengthText : "The minimum length for this field is {0}",
39774     /**
39775      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39776      */
39777     maxLengthText : "The maximum length for this field is {0}",
39778     /**
39779      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39780      */
39781     selectOnFocus : false,
39782     /**
39783      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39784      */    
39785     allowLeadingSpace : false,
39786     /**
39787      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39788      */
39789     blankText : "This field is required",
39790     /**
39791      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39792      * If available, this function will be called only after the basic validators all return true, and will be passed the
39793      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39794      */
39795     validator : null,
39796     /**
39797      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39798      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39799      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39800      */
39801     regex : null,
39802     /**
39803      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39804      */
39805     regexText : "",
39806     /**
39807      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39808      */
39809     emptyText : null,
39810    
39811
39812     // private
39813     initEvents : function()
39814     {
39815         if (this.emptyText) {
39816             this.el.attr('placeholder', this.emptyText);
39817         }
39818         
39819         Roo.form.TextField.superclass.initEvents.call(this);
39820         if(this.validationEvent == 'keyup'){
39821             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39822             this.el.on('keyup', this.filterValidation, this);
39823         }
39824         else if(this.validationEvent !== false){
39825             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39826         }
39827         
39828         if(this.selectOnFocus){
39829             this.on("focus", this.preFocus, this);
39830         }
39831         if (!this.allowLeadingSpace) {
39832             this.on('blur', this.cleanLeadingSpace, this);
39833         }
39834         
39835         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39836             this.el.on("keypress", this.filterKeys, this);
39837         }
39838         if(this.grow){
39839             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39840             this.el.on("click", this.autoSize,  this);
39841         }
39842         if(this.el.is('input[type=password]') && Roo.isSafari){
39843             this.el.on('keydown', this.SafariOnKeyDown, this);
39844         }
39845     },
39846
39847     processValue : function(value){
39848         if(this.stripCharsRe){
39849             var newValue = value.replace(this.stripCharsRe, '');
39850             if(newValue !== value){
39851                 this.setRawValue(newValue);
39852                 return newValue;
39853             }
39854         }
39855         return value;
39856     },
39857
39858     filterValidation : function(e){
39859         if(!e.isNavKeyPress()){
39860             this.validationTask.delay(this.validationDelay);
39861         }
39862     },
39863
39864     // private
39865     onKeyUp : function(e){
39866         if(!e.isNavKeyPress()){
39867             this.autoSize();
39868         }
39869     },
39870     // private - clean the leading white space
39871     cleanLeadingSpace : function(e)
39872     {
39873         if ( this.inputType == 'file') {
39874             return;
39875         }
39876         
39877         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39878     },
39879     /**
39880      * Resets the current field value to the originally-loaded value and clears any validation messages.
39881      *  
39882      */
39883     reset : function(){
39884         Roo.form.TextField.superclass.reset.call(this);
39885        
39886     }, 
39887     // private
39888     preFocus : function(){
39889         
39890         if(this.selectOnFocus){
39891             this.el.dom.select();
39892         }
39893     },
39894
39895     
39896     // private
39897     filterKeys : function(e){
39898         var k = e.getKey();
39899         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39900             return;
39901         }
39902         var c = e.getCharCode(), cc = String.fromCharCode(c);
39903         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39904             return;
39905         }
39906         if(!this.maskRe.test(cc)){
39907             e.stopEvent();
39908         }
39909     },
39910
39911     setValue : function(v){
39912         
39913         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39914         
39915         this.autoSize();
39916     },
39917
39918     /**
39919      * Validates a value according to the field's validation rules and marks the field as invalid
39920      * if the validation fails
39921      * @param {Mixed} value The value to validate
39922      * @return {Boolean} True if the value is valid, else false
39923      */
39924     validateValue : function(value){
39925         if(value.length < 1)  { // if it's blank
39926              if(this.allowBlank){
39927                 this.clearInvalid();
39928                 return true;
39929              }else{
39930                 this.markInvalid(this.blankText);
39931                 return false;
39932              }
39933         }
39934         if(value.length < this.minLength){
39935             this.markInvalid(String.format(this.minLengthText, this.minLength));
39936             return false;
39937         }
39938         if(value.length > this.maxLength){
39939             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39940             return false;
39941         }
39942         if(this.vtype){
39943             var vt = Roo.form.VTypes;
39944             if(!vt[this.vtype](value, this)){
39945                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39946                 return false;
39947             }
39948         }
39949         if(typeof this.validator == "function"){
39950             var msg = this.validator(value);
39951             if(msg !== true){
39952                 this.markInvalid(msg);
39953                 return false;
39954             }
39955         }
39956         if(this.regex && !this.regex.test(value)){
39957             this.markInvalid(this.regexText);
39958             return false;
39959         }
39960         return true;
39961     },
39962
39963     /**
39964      * Selects text in this field
39965      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39966      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39967      */
39968     selectText : function(start, end){
39969         var v = this.getRawValue();
39970         if(v.length > 0){
39971             start = start === undefined ? 0 : start;
39972             end = end === undefined ? v.length : end;
39973             var d = this.el.dom;
39974             if(d.setSelectionRange){
39975                 d.setSelectionRange(start, end);
39976             }else if(d.createTextRange){
39977                 var range = d.createTextRange();
39978                 range.moveStart("character", start);
39979                 range.moveEnd("character", v.length-end);
39980                 range.select();
39981             }
39982         }
39983     },
39984
39985     /**
39986      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39987      * This only takes effect if grow = true, and fires the autosize event.
39988      */
39989     autoSize : function(){
39990         if(!this.grow || !this.rendered){
39991             return;
39992         }
39993         if(!this.metrics){
39994             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39995         }
39996         var el = this.el;
39997         var v = el.dom.value;
39998         var d = document.createElement('div');
39999         d.appendChild(document.createTextNode(v));
40000         v = d.innerHTML;
40001         d = null;
40002         v += "&#160;";
40003         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40004         this.el.setWidth(w);
40005         this.fireEvent("autosize", this, w);
40006     },
40007     
40008     // private
40009     SafariOnKeyDown : function(event)
40010     {
40011         // this is a workaround for a password hang bug on chrome/ webkit.
40012         
40013         var isSelectAll = false;
40014         
40015         if(this.el.dom.selectionEnd > 0){
40016             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40017         }
40018         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40019             event.preventDefault();
40020             this.setValue('');
40021             return;
40022         }
40023         
40024         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40025             
40026             event.preventDefault();
40027             // this is very hacky as keydown always get's upper case.
40028             
40029             var cc = String.fromCharCode(event.getCharCode());
40030             
40031             
40032             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40033             
40034         }
40035         
40036         
40037     }
40038 });/*
40039  * Based on:
40040  * Ext JS Library 1.1.1
40041  * Copyright(c) 2006-2007, Ext JS, LLC.
40042  *
40043  * Originally Released Under LGPL - original licence link has changed is not relivant.
40044  *
40045  * Fork - LGPL
40046  * <script type="text/javascript">
40047  */
40048  
40049 /**
40050  * @class Roo.form.Hidden
40051  * @extends Roo.form.TextField
40052  * Simple Hidden element used on forms 
40053  * 
40054  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40055  * 
40056  * @constructor
40057  * Creates a new Hidden form element.
40058  * @param {Object} config Configuration options
40059  */
40060
40061
40062
40063 // easy hidden field...
40064 Roo.form.Hidden = function(config){
40065     Roo.form.Hidden.superclass.constructor.call(this, config);
40066 };
40067   
40068 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40069     fieldLabel:      '',
40070     inputType:      'hidden',
40071     width:          50,
40072     allowBlank:     true,
40073     labelSeparator: '',
40074     hidden:         true,
40075     itemCls :       'x-form-item-display-none'
40076
40077
40078 });
40079
40080
40081 /*
40082  * Based on:
40083  * Ext JS Library 1.1.1
40084  * Copyright(c) 2006-2007, Ext JS, LLC.
40085  *
40086  * Originally Released Under LGPL - original licence link has changed is not relivant.
40087  *
40088  * Fork - LGPL
40089  * <script type="text/javascript">
40090  */
40091  
40092 /**
40093  * @class Roo.form.TriggerField
40094  * @extends Roo.form.TextField
40095  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40096  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40097  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40098  * for which you can provide a custom implementation.  For example:
40099  * <pre><code>
40100 var trigger = new Roo.form.TriggerField();
40101 trigger.onTriggerClick = myTriggerFn;
40102 trigger.applyTo('my-field');
40103 </code></pre>
40104  *
40105  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40106  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40107  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40108  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40109  * @constructor
40110  * Create a new TriggerField.
40111  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40112  * to the base TextField)
40113  */
40114 Roo.form.TriggerField = function(config){
40115     this.mimicing = false;
40116     Roo.form.TriggerField.superclass.constructor.call(this, config);
40117 };
40118
40119 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40120     /**
40121      * @cfg {String} triggerClass A CSS class to apply to the trigger
40122      */
40123     /**
40124      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40125      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40126      */
40127     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40128     /**
40129      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40130      */
40131     hideTrigger:false,
40132
40133     /** @cfg {Boolean} grow @hide */
40134     /** @cfg {Number} growMin @hide */
40135     /** @cfg {Number} growMax @hide */
40136
40137     /**
40138      * @hide 
40139      * @method
40140      */
40141     autoSize: Roo.emptyFn,
40142     // private
40143     monitorTab : true,
40144     // private
40145     deferHeight : true,
40146
40147     
40148     actionMode : 'wrap',
40149     // private
40150     onResize : function(w, h){
40151         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40152         if(typeof w == 'number'){
40153             var x = w - this.trigger.getWidth();
40154             this.el.setWidth(this.adjustWidth('input', x));
40155             this.trigger.setStyle('left', x+'px');
40156         }
40157     },
40158
40159     // private
40160     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40161
40162     // private
40163     getResizeEl : function(){
40164         return this.wrap;
40165     },
40166
40167     // private
40168     getPositionEl : function(){
40169         return this.wrap;
40170     },
40171
40172     // private
40173     alignErrorIcon : function(){
40174         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40175     },
40176
40177     // private
40178     onRender : function(ct, position){
40179         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40180         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40181         this.trigger = this.wrap.createChild(this.triggerConfig ||
40182                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40183         if(this.hideTrigger){
40184             this.trigger.setDisplayed(false);
40185         }
40186         this.initTrigger();
40187         if(!this.width){
40188             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40189         }
40190     },
40191
40192     // private
40193     initTrigger : function(){
40194         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40195         this.trigger.addClassOnOver('x-form-trigger-over');
40196         this.trigger.addClassOnClick('x-form-trigger-click');
40197     },
40198
40199     // private
40200     onDestroy : function(){
40201         if(this.trigger){
40202             this.trigger.removeAllListeners();
40203             this.trigger.remove();
40204         }
40205         if(this.wrap){
40206             this.wrap.remove();
40207         }
40208         Roo.form.TriggerField.superclass.onDestroy.call(this);
40209     },
40210
40211     // private
40212     onFocus : function(){
40213         Roo.form.TriggerField.superclass.onFocus.call(this);
40214         if(!this.mimicing){
40215             this.wrap.addClass('x-trigger-wrap-focus');
40216             this.mimicing = true;
40217             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40218             if(this.monitorTab){
40219                 this.el.on("keydown", this.checkTab, this);
40220             }
40221         }
40222     },
40223
40224     // private
40225     checkTab : function(e){
40226         if(e.getKey() == e.TAB){
40227             this.triggerBlur();
40228         }
40229     },
40230
40231     // private
40232     onBlur : function(){
40233         // do nothing
40234     },
40235
40236     // private
40237     mimicBlur : function(e, t){
40238         if(!this.wrap.contains(t) && this.validateBlur()){
40239             this.triggerBlur();
40240         }
40241     },
40242
40243     // private
40244     triggerBlur : function(){
40245         this.mimicing = false;
40246         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40247         if(this.monitorTab){
40248             this.el.un("keydown", this.checkTab, this);
40249         }
40250         this.wrap.removeClass('x-trigger-wrap-focus');
40251         Roo.form.TriggerField.superclass.onBlur.call(this);
40252     },
40253
40254     // private
40255     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40256     validateBlur : function(e, t){
40257         return true;
40258     },
40259
40260     // private
40261     onDisable : function(){
40262         Roo.form.TriggerField.superclass.onDisable.call(this);
40263         if(this.wrap){
40264             this.wrap.addClass('x-item-disabled');
40265         }
40266     },
40267
40268     // private
40269     onEnable : function(){
40270         Roo.form.TriggerField.superclass.onEnable.call(this);
40271         if(this.wrap){
40272             this.wrap.removeClass('x-item-disabled');
40273         }
40274     },
40275
40276     // private
40277     onShow : function(){
40278         var ae = this.getActionEl();
40279         
40280         if(ae){
40281             ae.dom.style.display = '';
40282             ae.dom.style.visibility = 'visible';
40283         }
40284     },
40285
40286     // private
40287     
40288     onHide : function(){
40289         var ae = this.getActionEl();
40290         ae.dom.style.display = 'none';
40291     },
40292
40293     /**
40294      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40295      * by an implementing function.
40296      * @method
40297      * @param {EventObject} e
40298      */
40299     onTriggerClick : Roo.emptyFn
40300 });
40301
40302 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40303 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40304 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40305 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40306     initComponent : function(){
40307         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40308
40309         this.triggerConfig = {
40310             tag:'span', cls:'x-form-twin-triggers', cn:[
40311             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40312             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40313         ]};
40314     },
40315
40316     getTrigger : function(index){
40317         return this.triggers[index];
40318     },
40319
40320     initTrigger : function(){
40321         var ts = this.trigger.select('.x-form-trigger', true);
40322         this.wrap.setStyle('overflow', 'hidden');
40323         var triggerField = this;
40324         ts.each(function(t, all, index){
40325             t.hide = function(){
40326                 var w = triggerField.wrap.getWidth();
40327                 this.dom.style.display = 'none';
40328                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40329             };
40330             t.show = function(){
40331                 var w = triggerField.wrap.getWidth();
40332                 this.dom.style.display = '';
40333                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40334             };
40335             var triggerIndex = 'Trigger'+(index+1);
40336
40337             if(this['hide'+triggerIndex]){
40338                 t.dom.style.display = 'none';
40339             }
40340             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40341             t.addClassOnOver('x-form-trigger-over');
40342             t.addClassOnClick('x-form-trigger-click');
40343         }, this);
40344         this.triggers = ts.elements;
40345     },
40346
40347     onTrigger1Click : Roo.emptyFn,
40348     onTrigger2Click : Roo.emptyFn
40349 });/*
40350  * Based on:
40351  * Ext JS Library 1.1.1
40352  * Copyright(c) 2006-2007, Ext JS, LLC.
40353  *
40354  * Originally Released Under LGPL - original licence link has changed is not relivant.
40355  *
40356  * Fork - LGPL
40357  * <script type="text/javascript">
40358  */
40359  
40360 /**
40361  * @class Roo.form.TextArea
40362  * @extends Roo.form.TextField
40363  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40364  * support for auto-sizing.
40365  * @constructor
40366  * Creates a new TextArea
40367  * @param {Object} config Configuration options
40368  */
40369 Roo.form.TextArea = function(config){
40370     Roo.form.TextArea.superclass.constructor.call(this, config);
40371     // these are provided exchanges for backwards compat
40372     // minHeight/maxHeight were replaced by growMin/growMax to be
40373     // compatible with TextField growing config values
40374     if(this.minHeight !== undefined){
40375         this.growMin = this.minHeight;
40376     }
40377     if(this.maxHeight !== undefined){
40378         this.growMax = this.maxHeight;
40379     }
40380 };
40381
40382 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40383     /**
40384      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40385      */
40386     growMin : 60,
40387     /**
40388      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40389      */
40390     growMax: 1000,
40391     /**
40392      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40393      * in the field (equivalent to setting overflow: hidden, defaults to false)
40394      */
40395     preventScrollbars: false,
40396     /**
40397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40398      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40399      */
40400
40401     // private
40402     onRender : function(ct, position){
40403         if(!this.el){
40404             this.defaultAutoCreate = {
40405                 tag: "textarea",
40406                 style:"width:300px;height:60px;",
40407                 autocomplete: "new-password"
40408             };
40409         }
40410         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40411         if(this.grow){
40412             this.textSizeEl = Roo.DomHelper.append(document.body, {
40413                 tag: "pre", cls: "x-form-grow-sizer"
40414             });
40415             if(this.preventScrollbars){
40416                 this.el.setStyle("overflow", "hidden");
40417             }
40418             this.el.setHeight(this.growMin);
40419         }
40420     },
40421
40422     onDestroy : function(){
40423         if(this.textSizeEl){
40424             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40425         }
40426         Roo.form.TextArea.superclass.onDestroy.call(this);
40427     },
40428
40429     // private
40430     onKeyUp : function(e){
40431         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40432             this.autoSize();
40433         }
40434     },
40435
40436     /**
40437      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40438      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40439      */
40440     autoSize : function(){
40441         if(!this.grow || !this.textSizeEl){
40442             return;
40443         }
40444         var el = this.el;
40445         var v = el.dom.value;
40446         var ts = this.textSizeEl;
40447
40448         ts.innerHTML = '';
40449         ts.appendChild(document.createTextNode(v));
40450         v = ts.innerHTML;
40451
40452         Roo.fly(ts).setWidth(this.el.getWidth());
40453         if(v.length < 1){
40454             v = "&#160;&#160;";
40455         }else{
40456             if(Roo.isIE){
40457                 v = v.replace(/\n/g, '<p>&#160;</p>');
40458             }
40459             v += "&#160;\n&#160;";
40460         }
40461         ts.innerHTML = v;
40462         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40463         if(h != this.lastHeight){
40464             this.lastHeight = h;
40465             this.el.setHeight(h);
40466             this.fireEvent("autosize", this, h);
40467         }
40468     }
40469 });/*
40470  * Based on:
40471  * Ext JS Library 1.1.1
40472  * Copyright(c) 2006-2007, Ext JS, LLC.
40473  *
40474  * Originally Released Under LGPL - original licence link has changed is not relivant.
40475  *
40476  * Fork - LGPL
40477  * <script type="text/javascript">
40478  */
40479  
40480
40481 /**
40482  * @class Roo.form.NumberField
40483  * @extends Roo.form.TextField
40484  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40485  * @constructor
40486  * Creates a new NumberField
40487  * @param {Object} config Configuration options
40488  */
40489 Roo.form.NumberField = function(config){
40490     Roo.form.NumberField.superclass.constructor.call(this, config);
40491 };
40492
40493 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40494     /**
40495      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40496      */
40497     fieldClass: "x-form-field x-form-num-field",
40498     /**
40499      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40500      */
40501     allowDecimals : true,
40502     /**
40503      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40504      */
40505     decimalSeparator : ".",
40506     /**
40507      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40508      */
40509     decimalPrecision : 2,
40510     /**
40511      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40512      */
40513     allowNegative : true,
40514     /**
40515      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40516      */
40517     minValue : Number.NEGATIVE_INFINITY,
40518     /**
40519      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40520      */
40521     maxValue : Number.MAX_VALUE,
40522     /**
40523      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40524      */
40525     minText : "The minimum value for this field is {0}",
40526     /**
40527      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40528      */
40529     maxText : "The maximum value for this field is {0}",
40530     /**
40531      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40532      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40533      */
40534     nanText : "{0} is not a valid number",
40535
40536     // private
40537     initEvents : function(){
40538         Roo.form.NumberField.superclass.initEvents.call(this);
40539         var allowed = "0123456789";
40540         if(this.allowDecimals){
40541             allowed += this.decimalSeparator;
40542         }
40543         if(this.allowNegative){
40544             allowed += "-";
40545         }
40546         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40547         var keyPress = function(e){
40548             var k = e.getKey();
40549             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40550                 return;
40551             }
40552             var c = e.getCharCode();
40553             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40554                 e.stopEvent();
40555             }
40556         };
40557         this.el.on("keypress", keyPress, this);
40558     },
40559
40560     // private
40561     validateValue : function(value){
40562         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40563             return false;
40564         }
40565         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40566              return true;
40567         }
40568         var num = this.parseValue(value);
40569         if(isNaN(num)){
40570             this.markInvalid(String.format(this.nanText, value));
40571             return false;
40572         }
40573         if(num < this.minValue){
40574             this.markInvalid(String.format(this.minText, this.minValue));
40575             return false;
40576         }
40577         if(num > this.maxValue){
40578             this.markInvalid(String.format(this.maxText, this.maxValue));
40579             return false;
40580         }
40581         return true;
40582     },
40583
40584     getValue : function(){
40585         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40586     },
40587
40588     // private
40589     parseValue : function(value){
40590         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40591         return isNaN(value) ? '' : value;
40592     },
40593
40594     // private
40595     fixPrecision : function(value){
40596         var nan = isNaN(value);
40597         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40598             return nan ? '' : value;
40599         }
40600         return parseFloat(value).toFixed(this.decimalPrecision);
40601     },
40602
40603     setValue : function(v){
40604         v = this.fixPrecision(v);
40605         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40606     },
40607
40608     // private
40609     decimalPrecisionFcn : function(v){
40610         return Math.floor(v);
40611     },
40612
40613     beforeBlur : function(){
40614         var v = this.parseValue(this.getRawValue());
40615         if(v){
40616             this.setValue(v);
40617         }
40618     }
40619 });/*
40620  * Based on:
40621  * Ext JS Library 1.1.1
40622  * Copyright(c) 2006-2007, Ext JS, LLC.
40623  *
40624  * Originally Released Under LGPL - original licence link has changed is not relivant.
40625  *
40626  * Fork - LGPL
40627  * <script type="text/javascript">
40628  */
40629  
40630 /**
40631  * @class Roo.form.DateField
40632  * @extends Roo.form.TriggerField
40633  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40634 * @constructor
40635 * Create a new DateField
40636 * @param {Object} config
40637  */
40638 Roo.form.DateField = function(config)
40639 {
40640     Roo.form.DateField.superclass.constructor.call(this, config);
40641     
40642       this.addEvents({
40643          
40644         /**
40645          * @event select
40646          * Fires when a date is selected
40647              * @param {Roo.form.DateField} combo This combo box
40648              * @param {Date} date The date selected
40649              */
40650         'select' : true
40651          
40652     });
40653     
40654     
40655     if(typeof this.minValue == "string") {
40656         this.minValue = this.parseDate(this.minValue);
40657     }
40658     if(typeof this.maxValue == "string") {
40659         this.maxValue = this.parseDate(this.maxValue);
40660     }
40661     this.ddMatch = null;
40662     if(this.disabledDates){
40663         var dd = this.disabledDates;
40664         var re = "(?:";
40665         for(var i = 0; i < dd.length; i++){
40666             re += dd[i];
40667             if(i != dd.length-1) {
40668                 re += "|";
40669             }
40670         }
40671         this.ddMatch = new RegExp(re + ")");
40672     }
40673 };
40674
40675 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40676     /**
40677      * @cfg {String} format
40678      * The default date format string which can be overriden for localization support.  The format must be
40679      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40680      */
40681     format : "m/d/y",
40682     /**
40683      * @cfg {String} altFormats
40684      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40685      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40686      */
40687     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40688     /**
40689      * @cfg {Array} disabledDays
40690      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40691      */
40692     disabledDays : null,
40693     /**
40694      * @cfg {String} disabledDaysText
40695      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40696      */
40697     disabledDaysText : "Disabled",
40698     /**
40699      * @cfg {Array} disabledDates
40700      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40701      * expression so they are very powerful. Some examples:
40702      * <ul>
40703      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40704      * <li>["03/08", "09/16"] would disable those days for every year</li>
40705      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40706      * <li>["03/../2006"] would disable every day in March 2006</li>
40707      * <li>["^03"] would disable every day in every March</li>
40708      * </ul>
40709      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40710      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40711      */
40712     disabledDates : null,
40713     /**
40714      * @cfg {String} disabledDatesText
40715      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40716      */
40717     disabledDatesText : "Disabled",
40718     /**
40719      * @cfg {Date/String} minValue
40720      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40721      * valid format (defaults to null).
40722      */
40723     minValue : null,
40724     /**
40725      * @cfg {Date/String} maxValue
40726      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40727      * valid format (defaults to null).
40728      */
40729     maxValue : null,
40730     /**
40731      * @cfg {String} minText
40732      * The error text to display when the date in the cell is before minValue (defaults to
40733      * 'The date in this field must be after {minValue}').
40734      */
40735     minText : "The date in this field must be equal to or after {0}",
40736     /**
40737      * @cfg {String} maxText
40738      * The error text to display when the date in the cell is after maxValue (defaults to
40739      * 'The date in this field must be before {maxValue}').
40740      */
40741     maxText : "The date in this field must be equal to or before {0}",
40742     /**
40743      * @cfg {String} invalidText
40744      * The error text to display when the date in the field is invalid (defaults to
40745      * '{value} is not a valid date - it must be in the format {format}').
40746      */
40747     invalidText : "{0} is not a valid date - it must be in the format {1}",
40748     /**
40749      * @cfg {String} triggerClass
40750      * An additional CSS class used to style the trigger button.  The trigger will always get the
40751      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40752      * which displays a calendar icon).
40753      */
40754     triggerClass : 'x-form-date-trigger',
40755     
40756
40757     /**
40758      * @cfg {Boolean} useIso
40759      * if enabled, then the date field will use a hidden field to store the 
40760      * real value as iso formated date. default (false)
40761      */ 
40762     useIso : false,
40763     /**
40764      * @cfg {String/Object} autoCreate
40765      * A DomHelper element spec, or true for a default element spec (defaults to
40766      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40767      */ 
40768     // private
40769     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40770     
40771     // private
40772     hiddenField: false,
40773     
40774     onRender : function(ct, position)
40775     {
40776         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40777         if (this.useIso) {
40778             //this.el.dom.removeAttribute('name'); 
40779             Roo.log("Changing name?");
40780             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40781             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40782                     'before', true);
40783             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40784             // prevent input submission
40785             this.hiddenName = this.name;
40786         }
40787             
40788             
40789     },
40790     
40791     // private
40792     validateValue : function(value)
40793     {
40794         value = this.formatDate(value);
40795         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40796             Roo.log('super failed');
40797             return false;
40798         }
40799         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40800              return true;
40801         }
40802         var svalue = value;
40803         value = this.parseDate(value);
40804         if(!value){
40805             Roo.log('parse date failed' + svalue);
40806             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40807             return false;
40808         }
40809         var time = value.getTime();
40810         if(this.minValue && time < this.minValue.getTime()){
40811             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40812             return false;
40813         }
40814         if(this.maxValue && time > this.maxValue.getTime()){
40815             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40816             return false;
40817         }
40818         if(this.disabledDays){
40819             var day = value.getDay();
40820             for(var i = 0; i < this.disabledDays.length; i++) {
40821                 if(day === this.disabledDays[i]){
40822                     this.markInvalid(this.disabledDaysText);
40823                     return false;
40824                 }
40825             }
40826         }
40827         var fvalue = this.formatDate(value);
40828         if(this.ddMatch && this.ddMatch.test(fvalue)){
40829             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40830             return false;
40831         }
40832         return true;
40833     },
40834
40835     // private
40836     // Provides logic to override the default TriggerField.validateBlur which just returns true
40837     validateBlur : function(){
40838         return !this.menu || !this.menu.isVisible();
40839     },
40840     
40841     getName: function()
40842     {
40843         // returns hidden if it's set..
40844         if (!this.rendered) {return ''};
40845         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40846         
40847     },
40848
40849     /**
40850      * Returns the current date value of the date field.
40851      * @return {Date} The date value
40852      */
40853     getValue : function(){
40854         
40855         return  this.hiddenField ?
40856                 this.hiddenField.value :
40857                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40858     },
40859
40860     /**
40861      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40862      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40863      * (the default format used is "m/d/y").
40864      * <br />Usage:
40865      * <pre><code>
40866 //All of these calls set the same date value (May 4, 2006)
40867
40868 //Pass a date object:
40869 var dt = new Date('5/4/06');
40870 dateField.setValue(dt);
40871
40872 //Pass a date string (default format):
40873 dateField.setValue('5/4/06');
40874
40875 //Pass a date string (custom format):
40876 dateField.format = 'Y-m-d';
40877 dateField.setValue('2006-5-4');
40878 </code></pre>
40879      * @param {String/Date} date The date or valid date string
40880      */
40881     setValue : function(date){
40882         if (this.hiddenField) {
40883             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40884         }
40885         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40886         // make sure the value field is always stored as a date..
40887         this.value = this.parseDate(date);
40888         
40889         
40890     },
40891
40892     // private
40893     parseDate : function(value){
40894         if(!value || value instanceof Date){
40895             return value;
40896         }
40897         var v = Date.parseDate(value, this.format);
40898          if (!v && this.useIso) {
40899             v = Date.parseDate(value, 'Y-m-d');
40900         }
40901         if(!v && this.altFormats){
40902             if(!this.altFormatsArray){
40903                 this.altFormatsArray = this.altFormats.split("|");
40904             }
40905             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40906                 v = Date.parseDate(value, this.altFormatsArray[i]);
40907             }
40908         }
40909         return v;
40910     },
40911
40912     // private
40913     formatDate : function(date, fmt){
40914         return (!date || !(date instanceof Date)) ?
40915                date : date.dateFormat(fmt || this.format);
40916     },
40917
40918     // private
40919     menuListeners : {
40920         select: function(m, d){
40921             
40922             this.setValue(d);
40923             this.fireEvent('select', this, d);
40924         },
40925         show : function(){ // retain focus styling
40926             this.onFocus();
40927         },
40928         hide : function(){
40929             this.focus.defer(10, this);
40930             var ml = this.menuListeners;
40931             this.menu.un("select", ml.select,  this);
40932             this.menu.un("show", ml.show,  this);
40933             this.menu.un("hide", ml.hide,  this);
40934         }
40935     },
40936
40937     // private
40938     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40939     onTriggerClick : function(){
40940         if(this.disabled){
40941             return;
40942         }
40943         if(this.menu == null){
40944             this.menu = new Roo.menu.DateMenu();
40945         }
40946         Roo.apply(this.menu.picker,  {
40947             showClear: this.allowBlank,
40948             minDate : this.minValue,
40949             maxDate : this.maxValue,
40950             disabledDatesRE : this.ddMatch,
40951             disabledDatesText : this.disabledDatesText,
40952             disabledDays : this.disabledDays,
40953             disabledDaysText : this.disabledDaysText,
40954             format : this.useIso ? 'Y-m-d' : this.format,
40955             minText : String.format(this.minText, this.formatDate(this.minValue)),
40956             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40957         });
40958         this.menu.on(Roo.apply({}, this.menuListeners, {
40959             scope:this
40960         }));
40961         this.menu.picker.setValue(this.getValue() || new Date());
40962         this.menu.show(this.el, "tl-bl?");
40963     },
40964
40965     beforeBlur : function(){
40966         var v = this.parseDate(this.getRawValue());
40967         if(v){
40968             this.setValue(v);
40969         }
40970     },
40971
40972     /*@
40973      * overide
40974      * 
40975      */
40976     isDirty : function() {
40977         if(this.disabled) {
40978             return false;
40979         }
40980         
40981         if(typeof(this.startValue) === 'undefined'){
40982             return false;
40983         }
40984         
40985         return String(this.getValue()) !== String(this.startValue);
40986         
40987     },
40988     // @overide
40989     cleanLeadingSpace : function(e)
40990     {
40991        return;
40992     }
40993     
40994 });/*
40995  * Based on:
40996  * Ext JS Library 1.1.1
40997  * Copyright(c) 2006-2007, Ext JS, LLC.
40998  *
40999  * Originally Released Under LGPL - original licence link has changed is not relivant.
41000  *
41001  * Fork - LGPL
41002  * <script type="text/javascript">
41003  */
41004  
41005 /**
41006  * @class Roo.form.MonthField
41007  * @extends Roo.form.TriggerField
41008  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41009 * @constructor
41010 * Create a new MonthField
41011 * @param {Object} config
41012  */
41013 Roo.form.MonthField = function(config){
41014     
41015     Roo.form.MonthField.superclass.constructor.call(this, config);
41016     
41017       this.addEvents({
41018          
41019         /**
41020          * @event select
41021          * Fires when a date is selected
41022              * @param {Roo.form.MonthFieeld} combo This combo box
41023              * @param {Date} date The date selected
41024              */
41025         'select' : true
41026          
41027     });
41028     
41029     
41030     if(typeof this.minValue == "string") {
41031         this.minValue = this.parseDate(this.minValue);
41032     }
41033     if(typeof this.maxValue == "string") {
41034         this.maxValue = this.parseDate(this.maxValue);
41035     }
41036     this.ddMatch = null;
41037     if(this.disabledDates){
41038         var dd = this.disabledDates;
41039         var re = "(?:";
41040         for(var i = 0; i < dd.length; i++){
41041             re += dd[i];
41042             if(i != dd.length-1) {
41043                 re += "|";
41044             }
41045         }
41046         this.ddMatch = new RegExp(re + ")");
41047     }
41048 };
41049
41050 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41051     /**
41052      * @cfg {String} format
41053      * The default date format string which can be overriden for localization support.  The format must be
41054      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41055      */
41056     format : "M Y",
41057     /**
41058      * @cfg {String} altFormats
41059      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41060      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41061      */
41062     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41063     /**
41064      * @cfg {Array} disabledDays
41065      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41066      */
41067     disabledDays : [0,1,2,3,4,5,6],
41068     /**
41069      * @cfg {String} disabledDaysText
41070      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41071      */
41072     disabledDaysText : "Disabled",
41073     /**
41074      * @cfg {Array} disabledDates
41075      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41076      * expression so they are very powerful. Some examples:
41077      * <ul>
41078      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41079      * <li>["03/08", "09/16"] would disable those days for every year</li>
41080      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41081      * <li>["03/../2006"] would disable every day in March 2006</li>
41082      * <li>["^03"] would disable every day in every March</li>
41083      * </ul>
41084      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41085      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41086      */
41087     disabledDates : null,
41088     /**
41089      * @cfg {String} disabledDatesText
41090      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41091      */
41092     disabledDatesText : "Disabled",
41093     /**
41094      * @cfg {Date/String} minValue
41095      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41096      * valid format (defaults to null).
41097      */
41098     minValue : null,
41099     /**
41100      * @cfg {Date/String} maxValue
41101      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41102      * valid format (defaults to null).
41103      */
41104     maxValue : null,
41105     /**
41106      * @cfg {String} minText
41107      * The error text to display when the date in the cell is before minValue (defaults to
41108      * 'The date in this field must be after {minValue}').
41109      */
41110     minText : "The date in this field must be equal to or after {0}",
41111     /**
41112      * @cfg {String} maxTextf
41113      * The error text to display when the date in the cell is after maxValue (defaults to
41114      * 'The date in this field must be before {maxValue}').
41115      */
41116     maxText : "The date in this field must be equal to or before {0}",
41117     /**
41118      * @cfg {String} invalidText
41119      * The error text to display when the date in the field is invalid (defaults to
41120      * '{value} is not a valid date - it must be in the format {format}').
41121      */
41122     invalidText : "{0} is not a valid date - it must be in the format {1}",
41123     /**
41124      * @cfg {String} triggerClass
41125      * An additional CSS class used to style the trigger button.  The trigger will always get the
41126      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41127      * which displays a calendar icon).
41128      */
41129     triggerClass : 'x-form-date-trigger',
41130     
41131
41132     /**
41133      * @cfg {Boolean} useIso
41134      * if enabled, then the date field will use a hidden field to store the 
41135      * real value as iso formated date. default (true)
41136      */ 
41137     useIso : true,
41138     /**
41139      * @cfg {String/Object} autoCreate
41140      * A DomHelper element spec, or true for a default element spec (defaults to
41141      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41142      */ 
41143     // private
41144     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41145     
41146     // private
41147     hiddenField: false,
41148     
41149     hideMonthPicker : false,
41150     
41151     onRender : function(ct, position)
41152     {
41153         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41154         if (this.useIso) {
41155             this.el.dom.removeAttribute('name'); 
41156             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41157                     'before', true);
41158             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41159             // prevent input submission
41160             this.hiddenName = this.name;
41161         }
41162             
41163             
41164     },
41165     
41166     // private
41167     validateValue : function(value)
41168     {
41169         value = this.formatDate(value);
41170         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41171             return false;
41172         }
41173         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41174              return true;
41175         }
41176         var svalue = value;
41177         value = this.parseDate(value);
41178         if(!value){
41179             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41180             return false;
41181         }
41182         var time = value.getTime();
41183         if(this.minValue && time < this.minValue.getTime()){
41184             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41185             return false;
41186         }
41187         if(this.maxValue && time > this.maxValue.getTime()){
41188             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41189             return false;
41190         }
41191         /*if(this.disabledDays){
41192             var day = value.getDay();
41193             for(var i = 0; i < this.disabledDays.length; i++) {
41194                 if(day === this.disabledDays[i]){
41195                     this.markInvalid(this.disabledDaysText);
41196                     return false;
41197                 }
41198             }
41199         }
41200         */
41201         var fvalue = this.formatDate(value);
41202         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41203             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41204             return false;
41205         }
41206         */
41207         return true;
41208     },
41209
41210     // private
41211     // Provides logic to override the default TriggerField.validateBlur which just returns true
41212     validateBlur : function(){
41213         return !this.menu || !this.menu.isVisible();
41214     },
41215
41216     /**
41217      * Returns the current date value of the date field.
41218      * @return {Date} The date value
41219      */
41220     getValue : function(){
41221         
41222         
41223         
41224         return  this.hiddenField ?
41225                 this.hiddenField.value :
41226                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41227     },
41228
41229     /**
41230      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41231      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41232      * (the default format used is "m/d/y").
41233      * <br />Usage:
41234      * <pre><code>
41235 //All of these calls set the same date value (May 4, 2006)
41236
41237 //Pass a date object:
41238 var dt = new Date('5/4/06');
41239 monthField.setValue(dt);
41240
41241 //Pass a date string (default format):
41242 monthField.setValue('5/4/06');
41243
41244 //Pass a date string (custom format):
41245 monthField.format = 'Y-m-d';
41246 monthField.setValue('2006-5-4');
41247 </code></pre>
41248      * @param {String/Date} date The date or valid date string
41249      */
41250     setValue : function(date){
41251         Roo.log('month setValue' + date);
41252         // can only be first of month..
41253         
41254         var val = this.parseDate(date);
41255         
41256         if (this.hiddenField) {
41257             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41258         }
41259         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41260         this.value = this.parseDate(date);
41261     },
41262
41263     // private
41264     parseDate : function(value){
41265         if(!value || value instanceof Date){
41266             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41267             return value;
41268         }
41269         var v = Date.parseDate(value, this.format);
41270         if (!v && this.useIso) {
41271             v = Date.parseDate(value, 'Y-m-d');
41272         }
41273         if (v) {
41274             // 
41275             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41276         }
41277         
41278         
41279         if(!v && this.altFormats){
41280             if(!this.altFormatsArray){
41281                 this.altFormatsArray = this.altFormats.split("|");
41282             }
41283             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41284                 v = Date.parseDate(value, this.altFormatsArray[i]);
41285             }
41286         }
41287         return v;
41288     },
41289
41290     // private
41291     formatDate : function(date, fmt){
41292         return (!date || !(date instanceof Date)) ?
41293                date : date.dateFormat(fmt || this.format);
41294     },
41295
41296     // private
41297     menuListeners : {
41298         select: function(m, d){
41299             this.setValue(d);
41300             this.fireEvent('select', this, d);
41301         },
41302         show : function(){ // retain focus styling
41303             this.onFocus();
41304         },
41305         hide : function(){
41306             this.focus.defer(10, this);
41307             var ml = this.menuListeners;
41308             this.menu.un("select", ml.select,  this);
41309             this.menu.un("show", ml.show,  this);
41310             this.menu.un("hide", ml.hide,  this);
41311         }
41312     },
41313     // private
41314     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41315     onTriggerClick : function(){
41316         if(this.disabled){
41317             return;
41318         }
41319         if(this.menu == null){
41320             this.menu = new Roo.menu.DateMenu();
41321            
41322         }
41323         
41324         Roo.apply(this.menu.picker,  {
41325             
41326             showClear: this.allowBlank,
41327             minDate : this.minValue,
41328             maxDate : this.maxValue,
41329             disabledDatesRE : this.ddMatch,
41330             disabledDatesText : this.disabledDatesText,
41331             
41332             format : this.useIso ? 'Y-m-d' : this.format,
41333             minText : String.format(this.minText, this.formatDate(this.minValue)),
41334             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41335             
41336         });
41337          this.menu.on(Roo.apply({}, this.menuListeners, {
41338             scope:this
41339         }));
41340        
41341         
41342         var m = this.menu;
41343         var p = m.picker;
41344         
41345         // hide month picker get's called when we called by 'before hide';
41346         
41347         var ignorehide = true;
41348         p.hideMonthPicker  = function(disableAnim){
41349             if (ignorehide) {
41350                 return;
41351             }
41352              if(this.monthPicker){
41353                 Roo.log("hideMonthPicker called");
41354                 if(disableAnim === true){
41355                     this.monthPicker.hide();
41356                 }else{
41357                     this.monthPicker.slideOut('t', {duration:.2});
41358                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41359                     p.fireEvent("select", this, this.value);
41360                     m.hide();
41361                 }
41362             }
41363         }
41364         
41365         Roo.log('picker set value');
41366         Roo.log(this.getValue());
41367         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41368         m.show(this.el, 'tl-bl?');
41369         ignorehide  = false;
41370         // this will trigger hideMonthPicker..
41371         
41372         
41373         // hidden the day picker
41374         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41375         
41376         
41377         
41378       
41379         
41380         p.showMonthPicker.defer(100, p);
41381     
41382         
41383        
41384     },
41385
41386     beforeBlur : function(){
41387         var v = this.parseDate(this.getRawValue());
41388         if(v){
41389             this.setValue(v);
41390         }
41391     }
41392
41393     /** @cfg {Boolean} grow @hide */
41394     /** @cfg {Number} growMin @hide */
41395     /** @cfg {Number} growMax @hide */
41396     /**
41397      * @hide
41398      * @method autoSize
41399      */
41400 });/*
41401  * Based on:
41402  * Ext JS Library 1.1.1
41403  * Copyright(c) 2006-2007, Ext JS, LLC.
41404  *
41405  * Originally Released Under LGPL - original licence link has changed is not relivant.
41406  *
41407  * Fork - LGPL
41408  * <script type="text/javascript">
41409  */
41410  
41411
41412 /**
41413  * @class Roo.form.ComboBox
41414  * @extends Roo.form.TriggerField
41415  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41416  * @constructor
41417  * Create a new ComboBox.
41418  * @param {Object} config Configuration options
41419  */
41420 Roo.form.ComboBox = function(config){
41421     Roo.form.ComboBox.superclass.constructor.call(this, config);
41422     this.addEvents({
41423         /**
41424          * @event expand
41425          * Fires when the dropdown list is expanded
41426              * @param {Roo.form.ComboBox} combo This combo box
41427              */
41428         'expand' : true,
41429         /**
41430          * @event collapse
41431          * Fires when the dropdown list is collapsed
41432              * @param {Roo.form.ComboBox} combo This combo box
41433              */
41434         'collapse' : true,
41435         /**
41436          * @event beforeselect
41437          * Fires before a list item is selected. Return false to cancel the selection.
41438              * @param {Roo.form.ComboBox} combo This combo box
41439              * @param {Roo.data.Record} record The data record returned from the underlying store
41440              * @param {Number} index The index of the selected item in the dropdown list
41441              */
41442         'beforeselect' : true,
41443         /**
41444          * @event select
41445          * Fires when a list item is selected
41446              * @param {Roo.form.ComboBox} combo This combo box
41447              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41448              * @param {Number} index The index of the selected item in the dropdown list
41449              */
41450         'select' : true,
41451         /**
41452          * @event beforequery
41453          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41454          * The event object passed has these properties:
41455              * @param {Roo.form.ComboBox} combo This combo box
41456              * @param {String} query The query
41457              * @param {Boolean} forceAll true to force "all" query
41458              * @param {Boolean} cancel true to cancel the query
41459              * @param {Object} e The query event object
41460              */
41461         'beforequery': true,
41462          /**
41463          * @event add
41464          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41465              * @param {Roo.form.ComboBox} combo This combo box
41466              */
41467         'add' : true,
41468         /**
41469          * @event edit
41470          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41471              * @param {Roo.form.ComboBox} combo This combo box
41472              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41473              */
41474         'edit' : true
41475         
41476         
41477     });
41478     if(this.transform){
41479         this.allowDomMove = false;
41480         var s = Roo.getDom(this.transform);
41481         if(!this.hiddenName){
41482             this.hiddenName = s.name;
41483         }
41484         if(!this.store){
41485             this.mode = 'local';
41486             var d = [], opts = s.options;
41487             for(var i = 0, len = opts.length;i < len; i++){
41488                 var o = opts[i];
41489                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41490                 if(o.selected) {
41491                     this.value = value;
41492                 }
41493                 d.push([value, o.text]);
41494             }
41495             this.store = new Roo.data.SimpleStore({
41496                 'id': 0,
41497                 fields: ['value', 'text'],
41498                 data : d
41499             });
41500             this.valueField = 'value';
41501             this.displayField = 'text';
41502         }
41503         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41504         if(!this.lazyRender){
41505             this.target = true;
41506             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41507             s.parentNode.removeChild(s); // remove it
41508             this.render(this.el.parentNode);
41509         }else{
41510             s.parentNode.removeChild(s); // remove it
41511         }
41512
41513     }
41514     if (this.store) {
41515         this.store = Roo.factory(this.store, Roo.data);
41516     }
41517     
41518     this.selectedIndex = -1;
41519     if(this.mode == 'local'){
41520         if(config.queryDelay === undefined){
41521             this.queryDelay = 10;
41522         }
41523         if(config.minChars === undefined){
41524             this.minChars = 0;
41525         }
41526     }
41527 };
41528
41529 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41530     /**
41531      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41532      */
41533     /**
41534      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41535      * rendering into an Roo.Editor, defaults to false)
41536      */
41537     /**
41538      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41539      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41540      */
41541     /**
41542      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41543      */
41544     /**
41545      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41546      * the dropdown list (defaults to undefined, with no header element)
41547      */
41548
41549      /**
41550      * @cfg {String/Roo.Template} tpl The template to use to render the output
41551      */
41552      
41553     // private
41554     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41555     /**
41556      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41557      */
41558     listWidth: undefined,
41559     /**
41560      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41561      * mode = 'remote' or 'text' if mode = 'local')
41562      */
41563     displayField: undefined,
41564     /**
41565      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41566      * mode = 'remote' or 'value' if mode = 'local'). 
41567      * Note: use of a valueField requires the user make a selection
41568      * in order for a value to be mapped.
41569      */
41570     valueField: undefined,
41571     
41572     
41573     /**
41574      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41575      * field's data value (defaults to the underlying DOM element's name)
41576      */
41577     hiddenName: undefined,
41578     /**
41579      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41580      */
41581     listClass: '',
41582     /**
41583      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41584      */
41585     selectedClass: 'x-combo-selected',
41586     /**
41587      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41588      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41589      * which displays a downward arrow icon).
41590      */
41591     triggerClass : 'x-form-arrow-trigger',
41592     /**
41593      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41594      */
41595     shadow:'sides',
41596     /**
41597      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41598      * anchor positions (defaults to 'tl-bl')
41599      */
41600     listAlign: 'tl-bl?',
41601     /**
41602      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41603      */
41604     maxHeight: 300,
41605     /**
41606      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41607      * query specified by the allQuery config option (defaults to 'query')
41608      */
41609     triggerAction: 'query',
41610     /**
41611      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41612      * (defaults to 4, does not apply if editable = false)
41613      */
41614     minChars : 4,
41615     /**
41616      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41617      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41618      */
41619     typeAhead: false,
41620     /**
41621      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41622      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41623      */
41624     queryDelay: 500,
41625     /**
41626      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41627      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41628      */
41629     pageSize: 0,
41630     /**
41631      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41632      * when editable = true (defaults to false)
41633      */
41634     selectOnFocus:false,
41635     /**
41636      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41637      */
41638     queryParam: 'query',
41639     /**
41640      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41641      * when mode = 'remote' (defaults to 'Loading...')
41642      */
41643     loadingText: 'Loading...',
41644     /**
41645      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41646      */
41647     resizable: false,
41648     /**
41649      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41650      */
41651     handleHeight : 8,
41652     /**
41653      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41654      * traditional select (defaults to true)
41655      */
41656     editable: true,
41657     /**
41658      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41659      */
41660     allQuery: '',
41661     /**
41662      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41663      */
41664     mode: 'remote',
41665     /**
41666      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41667      * listWidth has a higher value)
41668      */
41669     minListWidth : 70,
41670     /**
41671      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41672      * allow the user to set arbitrary text into the field (defaults to false)
41673      */
41674     forceSelection:false,
41675     /**
41676      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41677      * if typeAhead = true (defaults to 250)
41678      */
41679     typeAheadDelay : 250,
41680     /**
41681      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41682      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41683      */
41684     valueNotFoundText : undefined,
41685     /**
41686      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41687      */
41688     blockFocus : false,
41689     
41690     /**
41691      * @cfg {Boolean} disableClear Disable showing of clear button.
41692      */
41693     disableClear : false,
41694     /**
41695      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41696      */
41697     alwaysQuery : false,
41698     
41699     //private
41700     addicon : false,
41701     editicon: false,
41702     
41703     // element that contains real text value.. (when hidden is used..)
41704      
41705     // private
41706     onRender : function(ct, position)
41707     {
41708         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41709         
41710         if(this.hiddenName){
41711             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41712                     'before', true);
41713             this.hiddenField.value =
41714                 this.hiddenValue !== undefined ? this.hiddenValue :
41715                 this.value !== undefined ? this.value : '';
41716
41717             // prevent input submission
41718             this.el.dom.removeAttribute('name');
41719              
41720              
41721         }
41722         
41723         if(Roo.isGecko){
41724             this.el.dom.setAttribute('autocomplete', 'off');
41725         }
41726
41727         var cls = 'x-combo-list';
41728
41729         this.list = new Roo.Layer({
41730             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41731         });
41732
41733         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41734         this.list.setWidth(lw);
41735         this.list.swallowEvent('mousewheel');
41736         this.assetHeight = 0;
41737
41738         if(this.title){
41739             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41740             this.assetHeight += this.header.getHeight();
41741         }
41742
41743         this.innerList = this.list.createChild({cls:cls+'-inner'});
41744         this.innerList.on('mouseover', this.onViewOver, this);
41745         this.innerList.on('mousemove', this.onViewMove, this);
41746         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41747         
41748         if(this.allowBlank && !this.pageSize && !this.disableClear){
41749             this.footer = this.list.createChild({cls:cls+'-ft'});
41750             this.pageTb = new Roo.Toolbar(this.footer);
41751            
41752         }
41753         if(this.pageSize){
41754             this.footer = this.list.createChild({cls:cls+'-ft'});
41755             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41756                     {pageSize: this.pageSize});
41757             
41758         }
41759         
41760         if (this.pageTb && this.allowBlank && !this.disableClear) {
41761             var _this = this;
41762             this.pageTb.add(new Roo.Toolbar.Fill(), {
41763                 cls: 'x-btn-icon x-btn-clear',
41764                 text: '&#160;',
41765                 handler: function()
41766                 {
41767                     _this.collapse();
41768                     _this.clearValue();
41769                     _this.onSelect(false, -1);
41770                 }
41771             });
41772         }
41773         if (this.footer) {
41774             this.assetHeight += this.footer.getHeight();
41775         }
41776         
41777
41778         if(!this.tpl){
41779             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41780         }
41781
41782         this.view = new Roo.View(this.innerList, this.tpl, {
41783             singleSelect:true,
41784             store: this.store,
41785             selectedClass: this.selectedClass
41786         });
41787
41788         this.view.on('click', this.onViewClick, this);
41789
41790         this.store.on('beforeload', this.onBeforeLoad, this);
41791         this.store.on('load', this.onLoad, this);
41792         this.store.on('loadexception', this.onLoadException, this);
41793
41794         if(this.resizable){
41795             this.resizer = new Roo.Resizable(this.list,  {
41796                pinned:true, handles:'se'
41797             });
41798             this.resizer.on('resize', function(r, w, h){
41799                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41800                 this.listWidth = w;
41801                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41802                 this.restrictHeight();
41803             }, this);
41804             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41805         }
41806         if(!this.editable){
41807             this.editable = true;
41808             this.setEditable(false);
41809         }  
41810         
41811         
41812         if (typeof(this.events.add.listeners) != 'undefined') {
41813             
41814             this.addicon = this.wrap.createChild(
41815                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41816        
41817             this.addicon.on('click', function(e) {
41818                 this.fireEvent('add', this);
41819             }, this);
41820         }
41821         if (typeof(this.events.edit.listeners) != 'undefined') {
41822             
41823             this.editicon = this.wrap.createChild(
41824                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41825             if (this.addicon) {
41826                 this.editicon.setStyle('margin-left', '40px');
41827             }
41828             this.editicon.on('click', function(e) {
41829                 
41830                 // we fire even  if inothing is selected..
41831                 this.fireEvent('edit', this, this.lastData );
41832                 
41833             }, this);
41834         }
41835         
41836         
41837         
41838     },
41839
41840     // private
41841     initEvents : function(){
41842         Roo.form.ComboBox.superclass.initEvents.call(this);
41843
41844         this.keyNav = new Roo.KeyNav(this.el, {
41845             "up" : function(e){
41846                 this.inKeyMode = true;
41847                 this.selectPrev();
41848             },
41849
41850             "down" : function(e){
41851                 if(!this.isExpanded()){
41852                     this.onTriggerClick();
41853                 }else{
41854                     this.inKeyMode = true;
41855                     this.selectNext();
41856                 }
41857             },
41858
41859             "enter" : function(e){
41860                 this.onViewClick();
41861                 //return true;
41862             },
41863
41864             "esc" : function(e){
41865                 this.collapse();
41866             },
41867
41868             "tab" : function(e){
41869                 this.onViewClick(false);
41870                 this.fireEvent("specialkey", this, e);
41871                 return true;
41872             },
41873
41874             scope : this,
41875
41876             doRelay : function(foo, bar, hname){
41877                 if(hname == 'down' || this.scope.isExpanded()){
41878                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41879                 }
41880                 return true;
41881             },
41882
41883             forceKeyDown: true
41884         });
41885         this.queryDelay = Math.max(this.queryDelay || 10,
41886                 this.mode == 'local' ? 10 : 250);
41887         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41888         if(this.typeAhead){
41889             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41890         }
41891         if(this.editable !== false){
41892             this.el.on("keyup", this.onKeyUp, this);
41893         }
41894         if(this.forceSelection){
41895             this.on('blur', this.doForce, this);
41896         }
41897     },
41898
41899     onDestroy : function(){
41900         if(this.view){
41901             this.view.setStore(null);
41902             this.view.el.removeAllListeners();
41903             this.view.el.remove();
41904             this.view.purgeListeners();
41905         }
41906         if(this.list){
41907             this.list.destroy();
41908         }
41909         if(this.store){
41910             this.store.un('beforeload', this.onBeforeLoad, this);
41911             this.store.un('load', this.onLoad, this);
41912             this.store.un('loadexception', this.onLoadException, this);
41913         }
41914         Roo.form.ComboBox.superclass.onDestroy.call(this);
41915     },
41916
41917     // private
41918     fireKey : function(e){
41919         if(e.isNavKeyPress() && !this.list.isVisible()){
41920             this.fireEvent("specialkey", this, e);
41921         }
41922     },
41923
41924     // private
41925     onResize: function(w, h){
41926         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41927         
41928         if(typeof w != 'number'){
41929             // we do not handle it!?!?
41930             return;
41931         }
41932         var tw = this.trigger.getWidth();
41933         tw += this.addicon ? this.addicon.getWidth() : 0;
41934         tw += this.editicon ? this.editicon.getWidth() : 0;
41935         var x = w - tw;
41936         this.el.setWidth( this.adjustWidth('input', x));
41937             
41938         this.trigger.setStyle('left', x+'px');
41939         
41940         if(this.list && this.listWidth === undefined){
41941             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41942             this.list.setWidth(lw);
41943             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41944         }
41945         
41946     
41947         
41948     },
41949
41950     /**
41951      * Allow or prevent the user from directly editing the field text.  If false is passed,
41952      * the user will only be able to select from the items defined in the dropdown list.  This method
41953      * is the runtime equivalent of setting the 'editable' config option at config time.
41954      * @param {Boolean} value True to allow the user to directly edit the field text
41955      */
41956     setEditable : function(value){
41957         if(value == this.editable){
41958             return;
41959         }
41960         this.editable = value;
41961         if(!value){
41962             this.el.dom.setAttribute('readOnly', true);
41963             this.el.on('mousedown', this.onTriggerClick,  this);
41964             this.el.addClass('x-combo-noedit');
41965         }else{
41966             this.el.dom.setAttribute('readOnly', false);
41967             this.el.un('mousedown', this.onTriggerClick,  this);
41968             this.el.removeClass('x-combo-noedit');
41969         }
41970     },
41971
41972     // private
41973     onBeforeLoad : function(){
41974         if(!this.hasFocus){
41975             return;
41976         }
41977         this.innerList.update(this.loadingText ?
41978                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41979         this.restrictHeight();
41980         this.selectedIndex = -1;
41981     },
41982
41983     // private
41984     onLoad : function(){
41985         if(!this.hasFocus){
41986             return;
41987         }
41988         if(this.store.getCount() > 0){
41989             this.expand();
41990             this.restrictHeight();
41991             if(this.lastQuery == this.allQuery){
41992                 if(this.editable){
41993                     this.el.dom.select();
41994                 }
41995                 if(!this.selectByValue(this.value, true)){
41996                     this.select(0, true);
41997                 }
41998             }else{
41999                 this.selectNext();
42000                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42001                     this.taTask.delay(this.typeAheadDelay);
42002                 }
42003             }
42004         }else{
42005             this.onEmptyResults();
42006         }
42007         //this.el.focus();
42008     },
42009     // private
42010     onLoadException : function()
42011     {
42012         this.collapse();
42013         Roo.log(this.store.reader.jsonData);
42014         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42015             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42016         }
42017         
42018         
42019     },
42020     // private
42021     onTypeAhead : function(){
42022         if(this.store.getCount() > 0){
42023             var r = this.store.getAt(0);
42024             var newValue = r.data[this.displayField];
42025             var len = newValue.length;
42026             var selStart = this.getRawValue().length;
42027             if(selStart != len){
42028                 this.setRawValue(newValue);
42029                 this.selectText(selStart, newValue.length);
42030             }
42031         }
42032     },
42033
42034     // private
42035     onSelect : function(record, index){
42036         if(this.fireEvent('beforeselect', this, record, index) !== false){
42037             this.setFromData(index > -1 ? record.data : false);
42038             this.collapse();
42039             this.fireEvent('select', this, record, index);
42040         }
42041     },
42042
42043     /**
42044      * Returns the currently selected field value or empty string if no value is set.
42045      * @return {String} value The selected value
42046      */
42047     getValue : function(){
42048         if(this.valueField){
42049             return typeof this.value != 'undefined' ? this.value : '';
42050         }
42051         return Roo.form.ComboBox.superclass.getValue.call(this);
42052     },
42053
42054     /**
42055      * Clears any text/value currently set in the field
42056      */
42057     clearValue : function(){
42058         if(this.hiddenField){
42059             this.hiddenField.value = '';
42060         }
42061         this.value = '';
42062         this.setRawValue('');
42063         this.lastSelectionText = '';
42064         
42065     },
42066
42067     /**
42068      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42069      * will be displayed in the field.  If the value does not match the data value of an existing item,
42070      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42071      * Otherwise the field will be blank (although the value will still be set).
42072      * @param {String} value The value to match
42073      */
42074     setValue : function(v){
42075         var text = v;
42076         if(this.valueField){
42077             var r = this.findRecord(this.valueField, v);
42078             if(r){
42079                 text = r.data[this.displayField];
42080             }else if(this.valueNotFoundText !== undefined){
42081                 text = this.valueNotFoundText;
42082             }
42083         }
42084         this.lastSelectionText = text;
42085         if(this.hiddenField){
42086             this.hiddenField.value = v;
42087         }
42088         Roo.form.ComboBox.superclass.setValue.call(this, text);
42089         this.value = v;
42090     },
42091     /**
42092      * @property {Object} the last set data for the element
42093      */
42094     
42095     lastData : false,
42096     /**
42097      * Sets the value of the field based on a object which is related to the record format for the store.
42098      * @param {Object} value the value to set as. or false on reset?
42099      */
42100     setFromData : function(o){
42101         var dv = ''; // display value
42102         var vv = ''; // value value..
42103         this.lastData = o;
42104         if (this.displayField) {
42105             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42106         } else {
42107             // this is an error condition!!!
42108             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42109         }
42110         
42111         if(this.valueField){
42112             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42113         }
42114         if(this.hiddenField){
42115             this.hiddenField.value = vv;
42116             
42117             this.lastSelectionText = dv;
42118             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42119             this.value = vv;
42120             return;
42121         }
42122         // no hidden field.. - we store the value in 'value', but still display
42123         // display field!!!!
42124         this.lastSelectionText = dv;
42125         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42126         this.value = vv;
42127         
42128         
42129     },
42130     // private
42131     reset : function(){
42132         // overridden so that last data is reset..
42133         this.setValue(this.resetValue);
42134         this.originalValue = this.getValue();
42135         this.clearInvalid();
42136         this.lastData = false;
42137         if (this.view) {
42138             this.view.clearSelections();
42139         }
42140     },
42141     // private
42142     findRecord : function(prop, value){
42143         var record;
42144         if(this.store.getCount() > 0){
42145             this.store.each(function(r){
42146                 if(r.data[prop] == value){
42147                     record = r;
42148                     return false;
42149                 }
42150                 return true;
42151             });
42152         }
42153         return record;
42154     },
42155     
42156     getName: function()
42157     {
42158         // returns hidden if it's set..
42159         if (!this.rendered) {return ''};
42160         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42161         
42162     },
42163     // private
42164     onViewMove : function(e, t){
42165         this.inKeyMode = false;
42166     },
42167
42168     // private
42169     onViewOver : function(e, t){
42170         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42171             return;
42172         }
42173         var item = this.view.findItemFromChild(t);
42174         if(item){
42175             var index = this.view.indexOf(item);
42176             this.select(index, false);
42177         }
42178     },
42179
42180     // private
42181     onViewClick : function(doFocus)
42182     {
42183         var index = this.view.getSelectedIndexes()[0];
42184         var r = this.store.getAt(index);
42185         if(r){
42186             this.onSelect(r, index);
42187         }
42188         if(doFocus !== false && !this.blockFocus){
42189             this.el.focus();
42190         }
42191     },
42192
42193     // private
42194     restrictHeight : function(){
42195         this.innerList.dom.style.height = '';
42196         var inner = this.innerList.dom;
42197         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42198         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42199         this.list.beginUpdate();
42200         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42201         this.list.alignTo(this.el, this.listAlign);
42202         this.list.endUpdate();
42203     },
42204
42205     // private
42206     onEmptyResults : function(){
42207         this.collapse();
42208     },
42209
42210     /**
42211      * Returns true if the dropdown list is expanded, else false.
42212      */
42213     isExpanded : function(){
42214         return this.list.isVisible();
42215     },
42216
42217     /**
42218      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42220      * @param {String} value The data value of the item to select
42221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42222      * selected item if it is not currently in view (defaults to true)
42223      * @return {Boolean} True if the value matched an item in the list, else false
42224      */
42225     selectByValue : function(v, scrollIntoView){
42226         if(v !== undefined && v !== null){
42227             var r = this.findRecord(this.valueField || this.displayField, v);
42228             if(r){
42229                 this.select(this.store.indexOf(r), scrollIntoView);
42230                 return true;
42231             }
42232         }
42233         return false;
42234     },
42235
42236     /**
42237      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42238      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42239      * @param {Number} index The zero-based index of the list item to select
42240      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42241      * selected item if it is not currently in view (defaults to true)
42242      */
42243     select : function(index, scrollIntoView){
42244         this.selectedIndex = index;
42245         this.view.select(index);
42246         if(scrollIntoView !== false){
42247             var el = this.view.getNode(index);
42248             if(el){
42249                 this.innerList.scrollChildIntoView(el, false);
42250             }
42251         }
42252     },
42253
42254     // private
42255     selectNext : function(){
42256         var ct = this.store.getCount();
42257         if(ct > 0){
42258             if(this.selectedIndex == -1){
42259                 this.select(0);
42260             }else if(this.selectedIndex < ct-1){
42261                 this.select(this.selectedIndex+1);
42262             }
42263         }
42264     },
42265
42266     // private
42267     selectPrev : function(){
42268         var ct = this.store.getCount();
42269         if(ct > 0){
42270             if(this.selectedIndex == -1){
42271                 this.select(0);
42272             }else if(this.selectedIndex != 0){
42273                 this.select(this.selectedIndex-1);
42274             }
42275         }
42276     },
42277
42278     // private
42279     onKeyUp : function(e){
42280         if(this.editable !== false && !e.isSpecialKey()){
42281             this.lastKey = e.getKey();
42282             this.dqTask.delay(this.queryDelay);
42283         }
42284     },
42285
42286     // private
42287     validateBlur : function(){
42288         return !this.list || !this.list.isVisible();   
42289     },
42290
42291     // private
42292     initQuery : function(){
42293         this.doQuery(this.getRawValue());
42294     },
42295
42296     // private
42297     doForce : function(){
42298         if(this.el.dom.value.length > 0){
42299             this.el.dom.value =
42300                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42301              
42302         }
42303     },
42304
42305     /**
42306      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42307      * query allowing the query action to be canceled if needed.
42308      * @param {String} query The SQL query to execute
42309      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42310      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42311      * saved in the current store (defaults to false)
42312      */
42313     doQuery : function(q, forceAll){
42314         if(q === undefined || q === null){
42315             q = '';
42316         }
42317         var qe = {
42318             query: q,
42319             forceAll: forceAll,
42320             combo: this,
42321             cancel:false
42322         };
42323         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42324             return false;
42325         }
42326         q = qe.query;
42327         forceAll = qe.forceAll;
42328         if(forceAll === true || (q.length >= this.minChars)){
42329             if(this.lastQuery != q || this.alwaysQuery){
42330                 this.lastQuery = q;
42331                 if(this.mode == 'local'){
42332                     this.selectedIndex = -1;
42333                     if(forceAll){
42334                         this.store.clearFilter();
42335                     }else{
42336                         this.store.filter(this.displayField, q);
42337                     }
42338                     this.onLoad();
42339                 }else{
42340                     this.store.baseParams[this.queryParam] = q;
42341                     this.store.load({
42342                         params: this.getParams(q)
42343                     });
42344                     this.expand();
42345                 }
42346             }else{
42347                 this.selectedIndex = -1;
42348                 this.onLoad();   
42349             }
42350         }
42351     },
42352
42353     // private
42354     getParams : function(q){
42355         var p = {};
42356         //p[this.queryParam] = q;
42357         if(this.pageSize){
42358             p.start = 0;
42359             p.limit = this.pageSize;
42360         }
42361         return p;
42362     },
42363
42364     /**
42365      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42366      */
42367     collapse : function(){
42368         if(!this.isExpanded()){
42369             return;
42370         }
42371         this.list.hide();
42372         Roo.get(document).un('mousedown', this.collapseIf, this);
42373         Roo.get(document).un('mousewheel', this.collapseIf, this);
42374         if (!this.editable) {
42375             Roo.get(document).un('keydown', this.listKeyPress, this);
42376         }
42377         this.fireEvent('collapse', this);
42378     },
42379
42380     // private
42381     collapseIf : function(e){
42382         if(!e.within(this.wrap) && !e.within(this.list)){
42383             this.collapse();
42384         }
42385     },
42386
42387     /**
42388      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42389      */
42390     expand : function(){
42391         if(this.isExpanded() || !this.hasFocus){
42392             return;
42393         }
42394         this.list.alignTo(this.el, this.listAlign);
42395         this.list.show();
42396         Roo.get(document).on('mousedown', this.collapseIf, this);
42397         Roo.get(document).on('mousewheel', this.collapseIf, this);
42398         if (!this.editable) {
42399             Roo.get(document).on('keydown', this.listKeyPress, this);
42400         }
42401         
42402         this.fireEvent('expand', this);
42403     },
42404
42405     // private
42406     // Implements the default empty TriggerField.onTriggerClick function
42407     onTriggerClick : function(){
42408         if(this.disabled){
42409             return;
42410         }
42411         if(this.isExpanded()){
42412             this.collapse();
42413             if (!this.blockFocus) {
42414                 this.el.focus();
42415             }
42416             
42417         }else {
42418             this.hasFocus = true;
42419             if(this.triggerAction == 'all') {
42420                 this.doQuery(this.allQuery, true);
42421             } else {
42422                 this.doQuery(this.getRawValue());
42423             }
42424             if (!this.blockFocus) {
42425                 this.el.focus();
42426             }
42427         }
42428     },
42429     listKeyPress : function(e)
42430     {
42431         //Roo.log('listkeypress');
42432         // scroll to first matching element based on key pres..
42433         if (e.isSpecialKey()) {
42434             return false;
42435         }
42436         var k = String.fromCharCode(e.getKey()).toUpperCase();
42437         //Roo.log(k);
42438         var match  = false;
42439         var csel = this.view.getSelectedNodes();
42440         var cselitem = false;
42441         if (csel.length) {
42442             var ix = this.view.indexOf(csel[0]);
42443             cselitem  = this.store.getAt(ix);
42444             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42445                 cselitem = false;
42446             }
42447             
42448         }
42449         
42450         this.store.each(function(v) { 
42451             if (cselitem) {
42452                 // start at existing selection.
42453                 if (cselitem.id == v.id) {
42454                     cselitem = false;
42455                 }
42456                 return;
42457             }
42458                 
42459             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42460                 match = this.store.indexOf(v);
42461                 return false;
42462             }
42463         }, this);
42464         
42465         if (match === false) {
42466             return true; // no more action?
42467         }
42468         // scroll to?
42469         this.view.select(match);
42470         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42471         sn.scrollIntoView(sn.dom.parentNode, false);
42472     } 
42473
42474     /** 
42475     * @cfg {Boolean} grow 
42476     * @hide 
42477     */
42478     /** 
42479     * @cfg {Number} growMin 
42480     * @hide 
42481     */
42482     /** 
42483     * @cfg {Number} growMax 
42484     * @hide 
42485     */
42486     /**
42487      * @hide
42488      * @method autoSize
42489      */
42490 });/*
42491  * Copyright(c) 2010-2012, Roo J Solutions Limited
42492  *
42493  * Licence LGPL
42494  *
42495  */
42496
42497 /**
42498  * @class Roo.form.ComboBoxArray
42499  * @extends Roo.form.TextField
42500  * A facebook style adder... for lists of email / people / countries  etc...
42501  * pick multiple items from a combo box, and shows each one.
42502  *
42503  *  Fred [x]  Brian [x]  [Pick another |v]
42504  *
42505  *
42506  *  For this to work: it needs various extra information
42507  *    - normal combo problay has
42508  *      name, hiddenName
42509  *    + displayField, valueField
42510  *
42511  *    For our purpose...
42512  *
42513  *
42514  *   If we change from 'extends' to wrapping...
42515  *   
42516  *  
42517  *
42518  
42519  
42520  * @constructor
42521  * Create a new ComboBoxArray.
42522  * @param {Object} config Configuration options
42523  */
42524  
42525
42526 Roo.form.ComboBoxArray = function(config)
42527 {
42528     this.addEvents({
42529         /**
42530          * @event beforeremove
42531          * Fires before remove the value from the list
42532              * @param {Roo.form.ComboBoxArray} _self This combo box array
42533              * @param {Roo.form.ComboBoxArray.Item} item removed item
42534              */
42535         'beforeremove' : true,
42536         /**
42537          * @event remove
42538          * Fires when remove the value from the list
42539              * @param {Roo.form.ComboBoxArray} _self This combo box array
42540              * @param {Roo.form.ComboBoxArray.Item} item removed item
42541              */
42542         'remove' : true
42543         
42544         
42545     });
42546     
42547     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42548     
42549     this.items = new Roo.util.MixedCollection(false);
42550     
42551     // construct the child combo...
42552     
42553     
42554     
42555     
42556    
42557     
42558 }
42559
42560  
42561 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42562
42563     /**
42564      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
42565      */
42566     
42567     lastData : false,
42568     
42569     // behavies liek a hiddne field
42570     inputType:      'hidden',
42571     /**
42572      * @cfg {Number} width The width of the box that displays the selected element
42573      */ 
42574     width:          300,
42575
42576     
42577     
42578     /**
42579      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42580      */
42581     name : false,
42582     /**
42583      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42584      */
42585     hiddenName : false,
42586       /**
42587      * @cfg {String} seperator    The value seperator normally ',' 
42588      */
42589     seperator : ',',
42590     
42591     // private the array of items that are displayed..
42592     items  : false,
42593     // private - the hidden field el.
42594     hiddenEl : false,
42595     // private - the filed el..
42596     el : false,
42597     
42598     //validateValue : function() { return true; }, // all values are ok!
42599     //onAddClick: function() { },
42600     
42601     onRender : function(ct, position) 
42602     {
42603         
42604         // create the standard hidden element
42605         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42606         
42607         
42608         // give fake names to child combo;
42609         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42610         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42611         
42612         this.combo = Roo.factory(this.combo, Roo.form);
42613         this.combo.onRender(ct, position);
42614         if (typeof(this.combo.width) != 'undefined') {
42615             this.combo.onResize(this.combo.width,0);
42616         }
42617         
42618         this.combo.initEvents();
42619         
42620         // assigned so form know we need to do this..
42621         this.store          = this.combo.store;
42622         this.valueField     = this.combo.valueField;
42623         this.displayField   = this.combo.displayField ;
42624         
42625         
42626         this.combo.wrap.addClass('x-cbarray-grp');
42627         
42628         var cbwrap = this.combo.wrap.createChild(
42629             {tag: 'div', cls: 'x-cbarray-cb'},
42630             this.combo.el.dom
42631         );
42632         
42633              
42634         this.hiddenEl = this.combo.wrap.createChild({
42635             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42636         });
42637         this.el = this.combo.wrap.createChild({
42638             tag: 'input',  type:'hidden' , name: this.name, value : ''
42639         });
42640          //   this.el.dom.removeAttribute("name");
42641         
42642         
42643         this.outerWrap = this.combo.wrap;
42644         this.wrap = cbwrap;
42645         
42646         this.outerWrap.setWidth(this.width);
42647         this.outerWrap.dom.removeChild(this.el.dom);
42648         
42649         this.wrap.dom.appendChild(this.el.dom);
42650         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42651         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42652         
42653         this.combo.trigger.setStyle('position','relative');
42654         this.combo.trigger.setStyle('left', '0px');
42655         this.combo.trigger.setStyle('top', '2px');
42656         
42657         this.combo.el.setStyle('vertical-align', 'text-bottom');
42658         
42659         //this.trigger.setStyle('vertical-align', 'top');
42660         
42661         // this should use the code from combo really... on('add' ....)
42662         if (this.adder) {
42663             
42664         
42665             this.adder = this.outerWrap.createChild(
42666                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42667             var _t = this;
42668             this.adder.on('click', function(e) {
42669                 _t.fireEvent('adderclick', this, e);
42670             }, _t);
42671         }
42672         //var _t = this;
42673         //this.adder.on('click', this.onAddClick, _t);
42674         
42675         
42676         this.combo.on('select', function(cb, rec, ix) {
42677             this.addItem(rec.data);
42678             
42679             cb.setValue('');
42680             cb.el.dom.value = '';
42681             //cb.lastData = rec.data;
42682             // add to list
42683             
42684         }, this);
42685         
42686         
42687     },
42688     
42689     
42690     getName: function()
42691     {
42692         // returns hidden if it's set..
42693         if (!this.rendered) {return ''};
42694         return  this.hiddenName ? this.hiddenName : this.name;
42695         
42696     },
42697     
42698     
42699     onResize: function(w, h){
42700         
42701         return;
42702         // not sure if this is needed..
42703         //this.combo.onResize(w,h);
42704         
42705         if(typeof w != 'number'){
42706             // we do not handle it!?!?
42707             return;
42708         }
42709         var tw = this.combo.trigger.getWidth();
42710         tw += this.addicon ? this.addicon.getWidth() : 0;
42711         tw += this.editicon ? this.editicon.getWidth() : 0;
42712         var x = w - tw;
42713         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42714             
42715         this.combo.trigger.setStyle('left', '0px');
42716         
42717         if(this.list && this.listWidth === undefined){
42718             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42719             this.list.setWidth(lw);
42720             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42721         }
42722         
42723     
42724         
42725     },
42726     
42727     addItem: function(rec)
42728     {
42729         var valueField = this.combo.valueField;
42730         var displayField = this.combo.displayField;
42731         
42732         if (this.items.indexOfKey(rec[valueField]) > -1) {
42733             //console.log("GOT " + rec.data.id);
42734             return;
42735         }
42736         
42737         var x = new Roo.form.ComboBoxArray.Item({
42738             //id : rec[this.idField],
42739             data : rec,
42740             displayField : displayField ,
42741             tipField : displayField ,
42742             cb : this
42743         });
42744         // use the 
42745         this.items.add(rec[valueField],x);
42746         // add it before the element..
42747         this.updateHiddenEl();
42748         x.render(this.outerWrap, this.wrap.dom);
42749         // add the image handler..
42750     },
42751     
42752     updateHiddenEl : function()
42753     {
42754         this.validate();
42755         if (!this.hiddenEl) {
42756             return;
42757         }
42758         var ar = [];
42759         var idField = this.combo.valueField;
42760         
42761         this.items.each(function(f) {
42762             ar.push(f.data[idField]);
42763         });
42764         this.hiddenEl.dom.value = ar.join(this.seperator);
42765         this.validate();
42766     },
42767     
42768     reset : function()
42769     {
42770         this.items.clear();
42771         
42772         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42773            el.remove();
42774         });
42775         
42776         this.el.dom.value = '';
42777         if (this.hiddenEl) {
42778             this.hiddenEl.dom.value = '';
42779         }
42780         
42781     },
42782     getValue: function()
42783     {
42784         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42785     },
42786     setValue: function(v) // not a valid action - must use addItems..
42787     {
42788         
42789         this.reset();
42790          
42791         if (this.store.isLocal && (typeof(v) == 'string')) {
42792             // then we can use the store to find the values..
42793             // comma seperated at present.. this needs to allow JSON based encoding..
42794             this.hiddenEl.value  = v;
42795             var v_ar = [];
42796             Roo.each(v.split(this.seperator), function(k) {
42797                 Roo.log("CHECK " + this.valueField + ',' + k);
42798                 var li = this.store.query(this.valueField, k);
42799                 if (!li.length) {
42800                     return;
42801                 }
42802                 var add = {};
42803                 add[this.valueField] = k;
42804                 add[this.displayField] = li.item(0).data[this.displayField];
42805                 
42806                 this.addItem(add);
42807             }, this) 
42808              
42809         }
42810         if (typeof(v) == 'object' ) {
42811             // then let's assume it's an array of objects..
42812             Roo.each(v, function(l) {
42813                 var add = l;
42814                 if (typeof(l) == 'string') {
42815                     add = {};
42816                     add[this.valueField] = l;
42817                     add[this.displayField] = l
42818                 }
42819                 this.addItem(add);
42820             }, this);
42821              
42822         }
42823         
42824         
42825     },
42826     setFromData: function(v)
42827     {
42828         // this recieves an object, if setValues is called.
42829         this.reset();
42830         this.el.dom.value = v[this.displayField];
42831         this.hiddenEl.dom.value = v[this.valueField];
42832         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42833             return;
42834         }
42835         var kv = v[this.valueField];
42836         var dv = v[this.displayField];
42837         kv = typeof(kv) != 'string' ? '' : kv;
42838         dv = typeof(dv) != 'string' ? '' : dv;
42839         
42840         
42841         var keys = kv.split(this.seperator);
42842         var display = dv.split(this.seperator);
42843         for (var i = 0 ; i < keys.length; i++) {
42844             add = {};
42845             add[this.valueField] = keys[i];
42846             add[this.displayField] = display[i];
42847             this.addItem(add);
42848         }
42849       
42850         
42851     },
42852     
42853     /**
42854      * Validates the combox array value
42855      * @return {Boolean} True if the value is valid, else false
42856      */
42857     validate : function(){
42858         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42859             this.clearInvalid();
42860             return true;
42861         }
42862         return false;
42863     },
42864     
42865     validateValue : function(value){
42866         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42867         
42868     },
42869     
42870     /*@
42871      * overide
42872      * 
42873      */
42874     isDirty : function() {
42875         if(this.disabled) {
42876             return false;
42877         }
42878         
42879         try {
42880             var d = Roo.decode(String(this.originalValue));
42881         } catch (e) {
42882             return String(this.getValue()) !== String(this.originalValue);
42883         }
42884         
42885         var originalValue = [];
42886         
42887         for (var i = 0; i < d.length; i++){
42888             originalValue.push(d[i][this.valueField]);
42889         }
42890         
42891         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42892         
42893     }
42894     
42895 });
42896
42897
42898
42899 /**
42900  * @class Roo.form.ComboBoxArray.Item
42901  * @extends Roo.BoxComponent
42902  * A selected item in the list
42903  *  Fred [x]  Brian [x]  [Pick another |v]
42904  * 
42905  * @constructor
42906  * Create a new item.
42907  * @param {Object} config Configuration options
42908  */
42909  
42910 Roo.form.ComboBoxArray.Item = function(config) {
42911     config.id = Roo.id();
42912     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42913 }
42914
42915 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42916     data : {},
42917     cb: false,
42918     displayField : false,
42919     tipField : false,
42920     
42921     
42922     defaultAutoCreate : {
42923         tag: 'div',
42924         cls: 'x-cbarray-item',
42925         cn : [ 
42926             { tag: 'div' },
42927             {
42928                 tag: 'img',
42929                 width:16,
42930                 height : 16,
42931                 src : Roo.BLANK_IMAGE_URL ,
42932                 align: 'center'
42933             }
42934         ]
42935         
42936     },
42937     
42938  
42939     onRender : function(ct, position)
42940     {
42941         Roo.form.Field.superclass.onRender.call(this, ct, position);
42942         
42943         if(!this.el){
42944             var cfg = this.getAutoCreate();
42945             this.el = ct.createChild(cfg, position);
42946         }
42947         
42948         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42949         
42950         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42951             this.cb.renderer(this.data) :
42952             String.format('{0}',this.data[this.displayField]);
42953         
42954             
42955         this.el.child('div').dom.setAttribute('qtip',
42956                         String.format('{0}',this.data[this.tipField])
42957         );
42958         
42959         this.el.child('img').on('click', this.remove, this);
42960         
42961     },
42962    
42963     remove : function()
42964     {
42965         if(this.cb.disabled){
42966             return;
42967         }
42968         
42969         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42970             this.cb.items.remove(this);
42971             this.el.child('img').un('click', this.remove, this);
42972             this.el.remove();
42973             this.cb.updateHiddenEl();
42974
42975             this.cb.fireEvent('remove', this.cb, this);
42976         }
42977         
42978     }
42979 });/*
42980  * RooJS Library 1.1.1
42981  * Copyright(c) 2008-2011  Alan Knowles
42982  *
42983  * License - LGPL
42984  */
42985  
42986
42987 /**
42988  * @class Roo.form.ComboNested
42989  * @extends Roo.form.ComboBox
42990  * A combobox for that allows selection of nested items in a list,
42991  * eg.
42992  *
42993  *  Book
42994  *    -> red
42995  *    -> green
42996  *  Table
42997  *    -> square
42998  *      ->red
42999  *      ->green
43000  *    -> rectangle
43001  *      ->green
43002  *      
43003  * 
43004  * @constructor
43005  * Create a new ComboNested
43006  * @param {Object} config Configuration options
43007  */
43008 Roo.form.ComboNested = function(config){
43009     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43010     // should verify some data...
43011     // like
43012     // hiddenName = required..
43013     // displayField = required
43014     // valudField == required
43015     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43016     var _t = this;
43017     Roo.each(req, function(e) {
43018         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43019             throw "Roo.form.ComboNested : missing value for: " + e;
43020         }
43021     });
43022      
43023     
43024 };
43025
43026 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43027    
43028     /*
43029      * @config {Number} max Number of columns to show
43030      */
43031     
43032     maxColumns : 3,
43033    
43034     list : null, // the outermost div..
43035     innerLists : null, // the
43036     views : null,
43037     stores : null,
43038     // private
43039     loadingChildren : false,
43040     
43041     onRender : function(ct, position)
43042     {
43043         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43044         
43045         if(this.hiddenName){
43046             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43047                     'before', true);
43048             this.hiddenField.value =
43049                 this.hiddenValue !== undefined ? this.hiddenValue :
43050                 this.value !== undefined ? this.value : '';
43051
43052             // prevent input submission
43053             this.el.dom.removeAttribute('name');
43054              
43055              
43056         }
43057         
43058         if(Roo.isGecko){
43059             this.el.dom.setAttribute('autocomplete', 'off');
43060         }
43061
43062         var cls = 'x-combo-list';
43063
43064         this.list = new Roo.Layer({
43065             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43066         });
43067
43068         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43069         this.list.setWidth(lw);
43070         this.list.swallowEvent('mousewheel');
43071         this.assetHeight = 0;
43072
43073         if(this.title){
43074             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43075             this.assetHeight += this.header.getHeight();
43076         }
43077         this.innerLists = [];
43078         this.views = [];
43079         this.stores = [];
43080         for (var i =0 ; i < this.maxColumns; i++) {
43081             this.onRenderList( cls, i);
43082         }
43083         
43084         // always needs footer, as we are going to have an 'OK' button.
43085         this.footer = this.list.createChild({cls:cls+'-ft'});
43086         this.pageTb = new Roo.Toolbar(this.footer);  
43087         var _this = this;
43088         this.pageTb.add(  {
43089             
43090             text: 'Done',
43091             handler: function()
43092             {
43093                 _this.collapse();
43094             }
43095         });
43096         
43097         if ( this.allowBlank && !this.disableClear) {
43098             
43099             this.pageTb.add(new Roo.Toolbar.Fill(), {
43100                 cls: 'x-btn-icon x-btn-clear',
43101                 text: '&#160;',
43102                 handler: function()
43103                 {
43104                     _this.collapse();
43105                     _this.clearValue();
43106                     _this.onSelect(false, -1);
43107                 }
43108             });
43109         }
43110         if (this.footer) {
43111             this.assetHeight += this.footer.getHeight();
43112         }
43113         
43114     },
43115     onRenderList : function (  cls, i)
43116     {
43117         
43118         var lw = Math.floor(
43119                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43120         );
43121         
43122         this.list.setWidth(lw); // default to '1'
43123
43124         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43125         //il.on('mouseover', this.onViewOver, this, { list:  i });
43126         //il.on('mousemove', this.onViewMove, this, { list:  i });
43127         il.setWidth(lw);
43128         il.setStyle({ 'overflow-x' : 'hidden'});
43129
43130         if(!this.tpl){
43131             this.tpl = new Roo.Template({
43132                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43133                 isEmpty: function (value, allValues) {
43134                     //Roo.log(value);
43135                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43136                     return dl ? 'has-children' : 'no-children'
43137                 }
43138             });
43139         }
43140         
43141         var store  = this.store;
43142         if (i > 0) {
43143             store  = new Roo.data.SimpleStore({
43144                 //fields : this.store.reader.meta.fields,
43145                 reader : this.store.reader,
43146                 data : [ ]
43147             });
43148         }
43149         this.stores[i]  = store;
43150                   
43151         var view = this.views[i] = new Roo.View(
43152             il,
43153             this.tpl,
43154             {
43155                 singleSelect:true,
43156                 store: store,
43157                 selectedClass: this.selectedClass
43158             }
43159         );
43160         view.getEl().setWidth(lw);
43161         view.getEl().setStyle({
43162             position: i < 1 ? 'relative' : 'absolute',
43163             top: 0,
43164             left: (i * lw ) + 'px',
43165             display : i > 0 ? 'none' : 'block'
43166         });
43167         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43168         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43169         //view.on('click', this.onViewClick, this, { list : i });
43170
43171         store.on('beforeload', this.onBeforeLoad, this);
43172         store.on('load',  this.onLoad, this, { list  : i});
43173         store.on('loadexception', this.onLoadException, this);
43174
43175         // hide the other vies..
43176         
43177         
43178         
43179     },
43180       
43181     restrictHeight : function()
43182     {
43183         var mh = 0;
43184         Roo.each(this.innerLists, function(il,i) {
43185             var el = this.views[i].getEl();
43186             el.dom.style.height = '';
43187             var inner = el.dom;
43188             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43189             // only adjust heights on other ones..
43190             mh = Math.max(h, mh);
43191             if (i < 1) {
43192                 
43193                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43194                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43195                
43196             }
43197             
43198             
43199         }, this);
43200         
43201         this.list.beginUpdate();
43202         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43203         this.list.alignTo(this.el, this.listAlign);
43204         this.list.endUpdate();
43205         
43206     },
43207      
43208     
43209     // -- store handlers..
43210     // private
43211     onBeforeLoad : function()
43212     {
43213         if(!this.hasFocus){
43214             return;
43215         }
43216         this.innerLists[0].update(this.loadingText ?
43217                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43218         this.restrictHeight();
43219         this.selectedIndex = -1;
43220     },
43221     // private
43222     onLoad : function(a,b,c,d)
43223     {
43224         if (!this.loadingChildren) {
43225             // then we are loading the top level. - hide the children
43226             for (var i = 1;i < this.views.length; i++) {
43227                 this.views[i].getEl().setStyle({ display : 'none' });
43228             }
43229             var lw = Math.floor(
43230                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43231             );
43232         
43233              this.list.setWidth(lw); // default to '1'
43234
43235             
43236         }
43237         if(!this.hasFocus){
43238             return;
43239         }
43240         
43241         if(this.store.getCount() > 0) {
43242             this.expand();
43243             this.restrictHeight();   
43244         } else {
43245             this.onEmptyResults();
43246         }
43247         
43248         if (!this.loadingChildren) {
43249             this.selectActive();
43250         }
43251         /*
43252         this.stores[1].loadData([]);
43253         this.stores[2].loadData([]);
43254         this.views
43255         */    
43256     
43257         //this.el.focus();
43258     },
43259     
43260     
43261     // private
43262     onLoadException : function()
43263     {
43264         this.collapse();
43265         Roo.log(this.store.reader.jsonData);
43266         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43267             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43268         }
43269         
43270         
43271     },
43272     // no cleaning of leading spaces on blur here.
43273     cleanLeadingSpace : function(e) { },
43274     
43275
43276     onSelectChange : function (view, sels, opts )
43277     {
43278         var ix = view.getSelectedIndexes();
43279          
43280         if (opts.list > this.maxColumns - 2) {
43281             if (view.store.getCount()<  1) {
43282                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43283
43284             } else  {
43285                 if (ix.length) {
43286                     // used to clear ?? but if we are loading unselected 
43287                     this.setFromData(view.store.getAt(ix[0]).data);
43288                 }
43289                 
43290             }
43291             
43292             return;
43293         }
43294         
43295         if (!ix.length) {
43296             // this get's fired when trigger opens..
43297            // this.setFromData({});
43298             var str = this.stores[opts.list+1];
43299             str.data.clear(); // removeall wihtout the fire events..
43300             return;
43301         }
43302         
43303         var rec = view.store.getAt(ix[0]);
43304          
43305         this.setFromData(rec.data);
43306         this.fireEvent('select', this, rec, ix[0]);
43307         
43308         var lw = Math.floor(
43309              (
43310                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43311              ) / this.maxColumns
43312         );
43313         this.loadingChildren = true;
43314         this.stores[opts.list+1].loadDataFromChildren( rec );
43315         this.loadingChildren = false;
43316         var dl = this.stores[opts.list+1]. getTotalCount();
43317         
43318         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43319         
43320         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43321         for (var i = opts.list+2; i < this.views.length;i++) {
43322             this.views[i].getEl().setStyle({ display : 'none' });
43323         }
43324         
43325         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43326         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43327         
43328         if (this.isLoading) {
43329            // this.selectActive(opts.list);
43330         }
43331          
43332     },
43333     
43334     
43335     
43336     
43337     onDoubleClick : function()
43338     {
43339         this.collapse(); //??
43340     },
43341     
43342      
43343     
43344     
43345     
43346     // private
43347     recordToStack : function(store, prop, value, stack)
43348     {
43349         var cstore = new Roo.data.SimpleStore({
43350             //fields : this.store.reader.meta.fields, // we need array reader.. for
43351             reader : this.store.reader,
43352             data : [ ]
43353         });
43354         var _this = this;
43355         var record  = false;
43356         var srec = false;
43357         if(store.getCount() < 1){
43358             return false;
43359         }
43360         store.each(function(r){
43361             if(r.data[prop] == value){
43362                 record = r;
43363             srec = r;
43364                 return false;
43365             }
43366             if (r.data.cn && r.data.cn.length) {
43367                 cstore.loadDataFromChildren( r);
43368                 var cret = _this.recordToStack(cstore, prop, value, stack);
43369                 if (cret !== false) {
43370                     record = cret;
43371                     srec = r;
43372                     return false;
43373                 }
43374             }
43375              
43376             return true;
43377         });
43378         if (record == false) {
43379             return false
43380         }
43381         stack.unshift(srec);
43382         return record;
43383     },
43384     
43385     /*
43386      * find the stack of stores that match our value.
43387      *
43388      * 
43389      */
43390     
43391     selectActive : function ()
43392     {
43393         // if store is not loaded, then we will need to wait for that to happen first.
43394         var stack = [];
43395         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43396         for (var i = 0; i < stack.length; i++ ) {
43397             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43398         }
43399         
43400     }
43401         
43402          
43403     
43404     
43405     
43406     
43407 });/*
43408  * Based on:
43409  * Ext JS Library 1.1.1
43410  * Copyright(c) 2006-2007, Ext JS, LLC.
43411  *
43412  * Originally Released Under LGPL - original licence link has changed is not relivant.
43413  *
43414  * Fork - LGPL
43415  * <script type="text/javascript">
43416  */
43417 /**
43418  * @class Roo.form.Checkbox
43419  * @extends Roo.form.Field
43420  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43421  * @constructor
43422  * Creates a new Checkbox
43423  * @param {Object} config Configuration options
43424  */
43425 Roo.form.Checkbox = function(config){
43426     Roo.form.Checkbox.superclass.constructor.call(this, config);
43427     this.addEvents({
43428         /**
43429          * @event check
43430          * Fires when the checkbox is checked or unchecked.
43431              * @param {Roo.form.Checkbox} this This checkbox
43432              * @param {Boolean} checked The new checked value
43433              */
43434         check : true
43435     });
43436 };
43437
43438 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43439     /**
43440      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43441      */
43442     focusClass : undefined,
43443     /**
43444      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43445      */
43446     fieldClass: "x-form-field",
43447     /**
43448      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43449      */
43450     checked: false,
43451     /**
43452      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43453      * {tag: "input", type: "checkbox", autocomplete: "off"})
43454      */
43455     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43456     /**
43457      * @cfg {String} boxLabel The text that appears beside the checkbox
43458      */
43459     boxLabel : "",
43460     /**
43461      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43462      */  
43463     inputValue : '1',
43464     /**
43465      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43466      */
43467      valueOff: '0', // value when not checked..
43468
43469     actionMode : 'viewEl', 
43470     //
43471     // private
43472     itemCls : 'x-menu-check-item x-form-item',
43473     groupClass : 'x-menu-group-item',
43474     inputType : 'hidden',
43475     
43476     
43477     inSetChecked: false, // check that we are not calling self...
43478     
43479     inputElement: false, // real input element?
43480     basedOn: false, // ????
43481     
43482     isFormField: true, // not sure where this is needed!!!!
43483
43484     onResize : function(){
43485         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43486         if(!this.boxLabel){
43487             this.el.alignTo(this.wrap, 'c-c');
43488         }
43489     },
43490
43491     initEvents : function(){
43492         Roo.form.Checkbox.superclass.initEvents.call(this);
43493         this.el.on("click", this.onClick,  this);
43494         this.el.on("change", this.onClick,  this);
43495     },
43496
43497
43498     getResizeEl : function(){
43499         return this.wrap;
43500     },
43501
43502     getPositionEl : function(){
43503         return this.wrap;
43504     },
43505
43506     // private
43507     onRender : function(ct, position){
43508         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43509         /*
43510         if(this.inputValue !== undefined){
43511             this.el.dom.value = this.inputValue;
43512         }
43513         */
43514         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43515         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43516         var viewEl = this.wrap.createChild({ 
43517             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43518         this.viewEl = viewEl;   
43519         this.wrap.on('click', this.onClick,  this); 
43520         
43521         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43522         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43523         
43524         
43525         
43526         if(this.boxLabel){
43527             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43528         //    viewEl.on('click', this.onClick,  this); 
43529         }
43530         //if(this.checked){
43531             this.setChecked(this.checked);
43532         //}else{
43533             //this.checked = this.el.dom;
43534         //}
43535
43536     },
43537
43538     // private
43539     initValue : Roo.emptyFn,
43540
43541     /**
43542      * Returns the checked state of the checkbox.
43543      * @return {Boolean} True if checked, else false
43544      */
43545     getValue : function(){
43546         if(this.el){
43547             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43548         }
43549         return this.valueOff;
43550         
43551     },
43552
43553         // private
43554     onClick : function(){ 
43555         if (this.disabled) {
43556             return;
43557         }
43558         this.setChecked(!this.checked);
43559
43560         //if(this.el.dom.checked != this.checked){
43561         //    this.setValue(this.el.dom.checked);
43562        // }
43563     },
43564
43565     /**
43566      * Sets the checked state of the checkbox.
43567      * On is always based on a string comparison between inputValue and the param.
43568      * @param {Boolean/String} value - the value to set 
43569      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43570      */
43571     setValue : function(v,suppressEvent){
43572         
43573         
43574         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43575         //if(this.el && this.el.dom){
43576         //    this.el.dom.checked = this.checked;
43577         //    this.el.dom.defaultChecked = this.checked;
43578         //}
43579         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43580         //this.fireEvent("check", this, this.checked);
43581     },
43582     // private..
43583     setChecked : function(state,suppressEvent)
43584     {
43585         if (this.inSetChecked) {
43586             this.checked = state;
43587             return;
43588         }
43589         
43590     
43591         if(this.wrap){
43592             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43593         }
43594         this.checked = state;
43595         if(suppressEvent !== true){
43596             this.fireEvent('check', this, state);
43597         }
43598         this.inSetChecked = true;
43599         this.el.dom.value = state ? this.inputValue : this.valueOff;
43600         this.inSetChecked = false;
43601         
43602     },
43603     // handle setting of hidden value by some other method!!?!?
43604     setFromHidden: function()
43605     {
43606         if(!this.el){
43607             return;
43608         }
43609         //console.log("SET FROM HIDDEN");
43610         //alert('setFrom hidden');
43611         this.setValue(this.el.dom.value);
43612     },
43613     
43614     onDestroy : function()
43615     {
43616         if(this.viewEl){
43617             Roo.get(this.viewEl).remove();
43618         }
43619          
43620         Roo.form.Checkbox.superclass.onDestroy.call(this);
43621     },
43622     
43623     setBoxLabel : function(str)
43624     {
43625         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43626     }
43627
43628 });/*
43629  * Based on:
43630  * Ext JS Library 1.1.1
43631  * Copyright(c) 2006-2007, Ext JS, LLC.
43632  *
43633  * Originally Released Under LGPL - original licence link has changed is not relivant.
43634  *
43635  * Fork - LGPL
43636  * <script type="text/javascript">
43637  */
43638  
43639 /**
43640  * @class Roo.form.Radio
43641  * @extends Roo.form.Checkbox
43642  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43643  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43644  * @constructor
43645  * Creates a new Radio
43646  * @param {Object} config Configuration options
43647  */
43648 Roo.form.Radio = function(){
43649     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43650 };
43651 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43652     inputType: 'radio',
43653
43654     /**
43655      * If this radio is part of a group, it will return the selected value
43656      * @return {String}
43657      */
43658     getGroupValue : function(){
43659         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43660     },
43661     
43662     
43663     onRender : function(ct, position){
43664         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43665         
43666         if(this.inputValue !== undefined){
43667             this.el.dom.value = this.inputValue;
43668         }
43669          
43670         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43671         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43672         //var viewEl = this.wrap.createChild({ 
43673         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43674         //this.viewEl = viewEl;   
43675         //this.wrap.on('click', this.onClick,  this); 
43676         
43677         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43678         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43679         
43680         
43681         
43682         if(this.boxLabel){
43683             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43684         //    viewEl.on('click', this.onClick,  this); 
43685         }
43686          if(this.checked){
43687             this.el.dom.checked =   'checked' ;
43688         }
43689          
43690     } 
43691     
43692     
43693 });//<script type="text/javascript">
43694
43695 /*
43696  * Based  Ext JS Library 1.1.1
43697  * Copyright(c) 2006-2007, Ext JS, LLC.
43698  * LGPL
43699  *
43700  */
43701  
43702 /**
43703  * @class Roo.HtmlEditorCore
43704  * @extends Roo.Component
43705  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43706  *
43707  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43708  */
43709
43710 Roo.HtmlEditorCore = function(config){
43711     
43712     
43713     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43714     
43715     
43716     this.addEvents({
43717         /**
43718          * @event initialize
43719          * Fires when the editor is fully initialized (including the iframe)
43720          * @param {Roo.HtmlEditorCore} this
43721          */
43722         initialize: true,
43723         /**
43724          * @event activate
43725          * Fires when the editor is first receives the focus. Any insertion must wait
43726          * until after this event.
43727          * @param {Roo.HtmlEditorCore} this
43728          */
43729         activate: true,
43730          /**
43731          * @event beforesync
43732          * Fires before the textarea is updated with content from the editor iframe. Return false
43733          * to cancel the sync.
43734          * @param {Roo.HtmlEditorCore} this
43735          * @param {String} html
43736          */
43737         beforesync: true,
43738          /**
43739          * @event beforepush
43740          * Fires before the iframe editor is updated with content from the textarea. Return false
43741          * to cancel the push.
43742          * @param {Roo.HtmlEditorCore} this
43743          * @param {String} html
43744          */
43745         beforepush: true,
43746          /**
43747          * @event sync
43748          * Fires when the textarea is updated with content from the editor iframe.
43749          * @param {Roo.HtmlEditorCore} this
43750          * @param {String} html
43751          */
43752         sync: true,
43753          /**
43754          * @event push
43755          * Fires when the iframe editor is updated with content from the textarea.
43756          * @param {Roo.HtmlEditorCore} this
43757          * @param {String} html
43758          */
43759         push: true,
43760         
43761         /**
43762          * @event editorevent
43763          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43764          * @param {Roo.HtmlEditorCore} this
43765          */
43766         editorevent: true
43767         
43768     });
43769     
43770     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43771     
43772     // defaults : white / black...
43773     this.applyBlacklists();
43774     
43775     
43776     
43777 };
43778
43779
43780 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43781
43782
43783      /**
43784      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43785      */
43786     
43787     owner : false,
43788     
43789      /**
43790      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43791      *                        Roo.resizable.
43792      */
43793     resizable : false,
43794      /**
43795      * @cfg {Number} height (in pixels)
43796      */   
43797     height: 300,
43798    /**
43799      * @cfg {Number} width (in pixels)
43800      */   
43801     width: 500,
43802     
43803     /**
43804      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43805      * 
43806      */
43807     stylesheets: false,
43808     
43809     /**
43810      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
43811      */
43812     allowComments: false,
43813     // id of frame..
43814     frameId: false,
43815     
43816     // private properties
43817     validationEvent : false,
43818     deferHeight: true,
43819     initialized : false,
43820     activated : false,
43821     sourceEditMode : false,
43822     onFocus : Roo.emptyFn,
43823     iframePad:3,
43824     hideMode:'offsets',
43825     
43826     clearUp: true,
43827     
43828     // blacklist + whitelisted elements..
43829     black: false,
43830     white: false,
43831      
43832     bodyCls : '',
43833
43834     /**
43835      * Protected method that will not generally be called directly. It
43836      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43837      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43838      */
43839     getDocMarkup : function(){
43840         // body styles..
43841         var st = '';
43842         
43843         // inherit styels from page...?? 
43844         if (this.stylesheets === false) {
43845             
43846             Roo.get(document.head).select('style').each(function(node) {
43847                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43848             });
43849             
43850             Roo.get(document.head).select('link').each(function(node) { 
43851                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43852             });
43853             
43854         } else if (!this.stylesheets.length) {
43855                 // simple..
43856                 st = '<style type="text/css">' +
43857                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43858                    '</style>';
43859         } else {
43860             for (var i in this.stylesheets) { 
43861                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43862             }
43863             
43864         }
43865         
43866         st +=  '<style type="text/css">' +
43867             'IMG { cursor: pointer } ' +
43868         '</style>';
43869
43870         var cls = 'roo-htmleditor-body';
43871         
43872         if(this.bodyCls.length){
43873             cls += ' ' + this.bodyCls;
43874         }
43875         
43876         return '<html><head>' + st  +
43877             //<style type="text/css">' +
43878             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43879             //'</style>' +
43880             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43881     },
43882
43883     // private
43884     onRender : function(ct, position)
43885     {
43886         var _t = this;
43887         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43888         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43889         
43890         
43891         this.el.dom.style.border = '0 none';
43892         this.el.dom.setAttribute('tabIndex', -1);
43893         this.el.addClass('x-hidden hide');
43894         
43895         
43896         
43897         if(Roo.isIE){ // fix IE 1px bogus margin
43898             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43899         }
43900        
43901         
43902         this.frameId = Roo.id();
43903         
43904          
43905         
43906         var iframe = this.owner.wrap.createChild({
43907             tag: 'iframe',
43908             cls: 'form-control', // bootstrap..
43909             id: this.frameId,
43910             name: this.frameId,
43911             frameBorder : 'no',
43912             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43913         }, this.el
43914         );
43915         
43916         
43917         this.iframe = iframe.dom;
43918
43919          this.assignDocWin();
43920         
43921         this.doc.designMode = 'on';
43922        
43923         this.doc.open();
43924         this.doc.write(this.getDocMarkup());
43925         this.doc.close();
43926
43927         
43928         var task = { // must defer to wait for browser to be ready
43929             run : function(){
43930                 //console.log("run task?" + this.doc.readyState);
43931                 this.assignDocWin();
43932                 if(this.doc.body || this.doc.readyState == 'complete'){
43933                     try {
43934                         this.doc.designMode="on";
43935                     } catch (e) {
43936                         return;
43937                     }
43938                     Roo.TaskMgr.stop(task);
43939                     this.initEditor.defer(10, this);
43940                 }
43941             },
43942             interval : 10,
43943             duration: 10000,
43944             scope: this
43945         };
43946         Roo.TaskMgr.start(task);
43947
43948     },
43949
43950     // private
43951     onResize : function(w, h)
43952     {
43953          Roo.log('resize: ' +w + ',' + h );
43954         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43955         if(!this.iframe){
43956             return;
43957         }
43958         if(typeof w == 'number'){
43959             
43960             this.iframe.style.width = w + 'px';
43961         }
43962         if(typeof h == 'number'){
43963             
43964             this.iframe.style.height = h + 'px';
43965             if(this.doc){
43966                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43967             }
43968         }
43969         
43970     },
43971
43972     /**
43973      * Toggles the editor between standard and source edit mode.
43974      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43975      */
43976     toggleSourceEdit : function(sourceEditMode){
43977         
43978         this.sourceEditMode = sourceEditMode === true;
43979         
43980         if(this.sourceEditMode){
43981  
43982             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43983             
43984         }else{
43985             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43986             //this.iframe.className = '';
43987             this.deferFocus();
43988         }
43989         //this.setSize(this.owner.wrap.getSize());
43990         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43991     },
43992
43993     
43994   
43995
43996     /**
43997      * Protected method that will not generally be called directly. If you need/want
43998      * custom HTML cleanup, this is the method you should override.
43999      * @param {String} html The HTML to be cleaned
44000      * return {String} The cleaned HTML
44001      */
44002     cleanHtml : function(html){
44003         html = String(html);
44004         if(html.length > 5){
44005             if(Roo.isSafari){ // strip safari nonsense
44006                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44007             }
44008         }
44009         if(html == '&nbsp;'){
44010             html = '';
44011         }
44012         return html;
44013     },
44014
44015     /**
44016      * HTML Editor -> Textarea
44017      * Protected method that will not generally be called directly. Syncs the contents
44018      * of the editor iframe with the textarea.
44019      */
44020     syncValue : function(){
44021         if(this.initialized){
44022             var bd = (this.doc.body || this.doc.documentElement);
44023             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44024             var html = bd.innerHTML;
44025             if(Roo.isSafari){
44026                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44027                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44028                 if(m && m[1]){
44029                     html = '<div style="'+m[0]+'">' + html + '</div>';
44030                 }
44031             }
44032             html = this.cleanHtml(html);
44033             // fix up the special chars.. normaly like back quotes in word...
44034             // however we do not want to do this with chinese..
44035             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44036                 
44037                 var cc = match.charCodeAt();
44038
44039                 // Get the character value, handling surrogate pairs
44040                 if (match.length == 2) {
44041                     // It's a surrogate pair, calculate the Unicode code point
44042                     var high = match.charCodeAt(0) - 0xD800;
44043                     var low  = match.charCodeAt(1) - 0xDC00;
44044                     cc = (high * 0x400) + low + 0x10000;
44045                 }  else if (
44046                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44047                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44048                     (cc >= 0xf900 && cc < 0xfb00 )
44049                 ) {
44050                         return match;
44051                 }  
44052          
44053                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44054                 return "&#" + cc + ";";
44055                 
44056                 
44057             });
44058             
44059             
44060              
44061             if(this.owner.fireEvent('beforesync', this, html) !== false){
44062                 this.el.dom.value = html;
44063                 this.owner.fireEvent('sync', this, html);
44064             }
44065         }
44066     },
44067
44068     /**
44069      * Protected method that will not generally be called directly. Pushes the value of the textarea
44070      * into the iframe editor.
44071      */
44072     pushValue : function(){
44073         if(this.initialized){
44074             var v = this.el.dom.value.trim();
44075             
44076 //            if(v.length < 1){
44077 //                v = '&#160;';
44078 //            }
44079             
44080             if(this.owner.fireEvent('beforepush', this, v) !== false){
44081                 var d = (this.doc.body || this.doc.documentElement);
44082                 d.innerHTML = v;
44083                 this.cleanUpPaste();
44084                 this.el.dom.value = d.innerHTML;
44085                 this.owner.fireEvent('push', this, v);
44086             }
44087         }
44088     },
44089
44090     // private
44091     deferFocus : function(){
44092         this.focus.defer(10, this);
44093     },
44094
44095     // doc'ed in Field
44096     focus : function(){
44097         if(this.win && !this.sourceEditMode){
44098             this.win.focus();
44099         }else{
44100             this.el.focus();
44101         }
44102     },
44103     
44104     assignDocWin: function()
44105     {
44106         var iframe = this.iframe;
44107         
44108          if(Roo.isIE){
44109             this.doc = iframe.contentWindow.document;
44110             this.win = iframe.contentWindow;
44111         } else {
44112 //            if (!Roo.get(this.frameId)) {
44113 //                return;
44114 //            }
44115 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44116 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44117             
44118             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44119                 return;
44120             }
44121             
44122             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44123             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44124         }
44125     },
44126     
44127     // private
44128     initEditor : function(){
44129         //console.log("INIT EDITOR");
44130         this.assignDocWin();
44131         
44132         
44133         
44134         this.doc.designMode="on";
44135         this.doc.open();
44136         this.doc.write(this.getDocMarkup());
44137         this.doc.close();
44138         
44139         var dbody = (this.doc.body || this.doc.documentElement);
44140         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44141         // this copies styles from the containing element into thsi one..
44142         // not sure why we need all of this..
44143         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44144         
44145         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44146         //ss['background-attachment'] = 'fixed'; // w3c
44147         dbody.bgProperties = 'fixed'; // ie
44148         //Roo.DomHelper.applyStyles(dbody, ss);
44149         Roo.EventManager.on(this.doc, {
44150             //'mousedown': this.onEditorEvent,
44151             'mouseup': this.onEditorEvent,
44152             'dblclick': this.onEditorEvent,
44153             'click': this.onEditorEvent,
44154             'keyup': this.onEditorEvent,
44155             buffer:100,
44156             scope: this
44157         });
44158         if(Roo.isGecko){
44159             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44160         }
44161         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44162             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44163         }
44164         this.initialized = true;
44165
44166         this.owner.fireEvent('initialize', this);
44167         this.pushValue();
44168     },
44169
44170     // private
44171     onDestroy : function(){
44172         
44173         
44174         
44175         if(this.rendered){
44176             
44177             //for (var i =0; i < this.toolbars.length;i++) {
44178             //    // fixme - ask toolbars for heights?
44179             //    this.toolbars[i].onDestroy();
44180            // }
44181             
44182             //this.wrap.dom.innerHTML = '';
44183             //this.wrap.remove();
44184         }
44185     },
44186
44187     // private
44188     onFirstFocus : function(){
44189         
44190         this.assignDocWin();
44191         
44192         
44193         this.activated = true;
44194          
44195     
44196         if(Roo.isGecko){ // prevent silly gecko errors
44197             this.win.focus();
44198             var s = this.win.getSelection();
44199             if(!s.focusNode || s.focusNode.nodeType != 3){
44200                 var r = s.getRangeAt(0);
44201                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44202                 r.collapse(true);
44203                 this.deferFocus();
44204             }
44205             try{
44206                 this.execCmd('useCSS', true);
44207                 this.execCmd('styleWithCSS', false);
44208             }catch(e){}
44209         }
44210         this.owner.fireEvent('activate', this);
44211     },
44212
44213     // private
44214     adjustFont: function(btn){
44215         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44216         //if(Roo.isSafari){ // safari
44217         //    adjust *= 2;
44218        // }
44219         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44220         if(Roo.isSafari){ // safari
44221             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44222             v =  (v < 10) ? 10 : v;
44223             v =  (v > 48) ? 48 : v;
44224             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44225             
44226         }
44227         
44228         
44229         v = Math.max(1, v+adjust);
44230         
44231         this.execCmd('FontSize', v  );
44232     },
44233
44234     onEditorEvent : function(e)
44235     {
44236         this.owner.fireEvent('editorevent', this, e);
44237       //  this.updateToolbar();
44238         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44239     },
44240
44241     insertTag : function(tg)
44242     {
44243         // could be a bit smarter... -> wrap the current selected tRoo..
44244         if (tg.toLowerCase() == 'span' ||
44245             tg.toLowerCase() == 'code' ||
44246             tg.toLowerCase() == 'sup' ||
44247             tg.toLowerCase() == 'sub' 
44248             ) {
44249             
44250             range = this.createRange(this.getSelection());
44251             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44252             wrappingNode.appendChild(range.extractContents());
44253             range.insertNode(wrappingNode);
44254
44255             return;
44256             
44257             
44258             
44259         }
44260         this.execCmd("formatblock",   tg);
44261         
44262     },
44263     
44264     insertText : function(txt)
44265     {
44266         
44267         
44268         var range = this.createRange();
44269         range.deleteContents();
44270                //alert(Sender.getAttribute('label'));
44271                
44272         range.insertNode(this.doc.createTextNode(txt));
44273     } ,
44274     
44275      
44276
44277     /**
44278      * Executes a Midas editor command on the editor document and performs necessary focus and
44279      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44280      * @param {String} cmd The Midas command
44281      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44282      */
44283     relayCmd : function(cmd, value){
44284         this.win.focus();
44285         this.execCmd(cmd, value);
44286         this.owner.fireEvent('editorevent', this);
44287         //this.updateToolbar();
44288         this.owner.deferFocus();
44289     },
44290
44291     /**
44292      * Executes a Midas editor command directly on the editor document.
44293      * For visual commands, you should use {@link #relayCmd} instead.
44294      * <b>This should only be called after the editor is initialized.</b>
44295      * @param {String} cmd The Midas command
44296      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44297      */
44298     execCmd : function(cmd, value){
44299         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44300         this.syncValue();
44301     },
44302  
44303  
44304    
44305     /**
44306      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44307      * to insert tRoo.
44308      * @param {String} text | dom node.. 
44309      */
44310     insertAtCursor : function(text)
44311     {
44312         
44313         if(!this.activated){
44314             return;
44315         }
44316         /*
44317         if(Roo.isIE){
44318             this.win.focus();
44319             var r = this.doc.selection.createRange();
44320             if(r){
44321                 r.collapse(true);
44322                 r.pasteHTML(text);
44323                 this.syncValue();
44324                 this.deferFocus();
44325             
44326             }
44327             return;
44328         }
44329         */
44330         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44331             this.win.focus();
44332             
44333             
44334             // from jquery ui (MIT licenced)
44335             var range, node;
44336             var win = this.win;
44337             
44338             if (win.getSelection && win.getSelection().getRangeAt) {
44339                 range = win.getSelection().getRangeAt(0);
44340                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44341                 range.insertNode(node);
44342             } else if (win.document.selection && win.document.selection.createRange) {
44343                 // no firefox support
44344                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44345                 win.document.selection.createRange().pasteHTML(txt);
44346             } else {
44347                 // no firefox support
44348                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44349                 this.execCmd('InsertHTML', txt);
44350             } 
44351             
44352             this.syncValue();
44353             
44354             this.deferFocus();
44355         }
44356     },
44357  // private
44358     mozKeyPress : function(e){
44359         if(e.ctrlKey){
44360             var c = e.getCharCode(), cmd;
44361           
44362             if(c > 0){
44363                 c = String.fromCharCode(c).toLowerCase();
44364                 switch(c){
44365                     case 'b':
44366                         cmd = 'bold';
44367                         break;
44368                     case 'i':
44369                         cmd = 'italic';
44370                         break;
44371                     
44372                     case 'u':
44373                         cmd = 'underline';
44374                         break;
44375                     
44376                     case 'v':
44377                         this.cleanUpPaste.defer(100, this);
44378                         return;
44379                         
44380                 }
44381                 if(cmd){
44382                     this.win.focus();
44383                     this.execCmd(cmd);
44384                     this.deferFocus();
44385                     e.preventDefault();
44386                 }
44387                 
44388             }
44389         }
44390     },
44391
44392     // private
44393     fixKeys : function(){ // load time branching for fastest keydown performance
44394         if(Roo.isIE){
44395             return function(e){
44396                 var k = e.getKey(), r;
44397                 if(k == e.TAB){
44398                     e.stopEvent();
44399                     r = this.doc.selection.createRange();
44400                     if(r){
44401                         r.collapse(true);
44402                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44403                         this.deferFocus();
44404                     }
44405                     return;
44406                 }
44407                 
44408                 if(k == e.ENTER){
44409                     r = this.doc.selection.createRange();
44410                     if(r){
44411                         var target = r.parentElement();
44412                         if(!target || target.tagName.toLowerCase() != 'li'){
44413                             e.stopEvent();
44414                             r.pasteHTML('<br />');
44415                             r.collapse(false);
44416                             r.select();
44417                         }
44418                     }
44419                 }
44420                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44421                     this.cleanUpPaste.defer(100, this);
44422                     return;
44423                 }
44424                 
44425                 
44426             };
44427         }else if(Roo.isOpera){
44428             return function(e){
44429                 var k = e.getKey();
44430                 if(k == e.TAB){
44431                     e.stopEvent();
44432                     this.win.focus();
44433                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44434                     this.deferFocus();
44435                 }
44436                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44437                     this.cleanUpPaste.defer(100, this);
44438                     return;
44439                 }
44440                 
44441             };
44442         }else if(Roo.isSafari){
44443             return function(e){
44444                 var k = e.getKey();
44445                 
44446                 if(k == e.TAB){
44447                     e.stopEvent();
44448                     this.execCmd('InsertText','\t');
44449                     this.deferFocus();
44450                     return;
44451                 }
44452                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44453                     this.cleanUpPaste.defer(100, this);
44454                     return;
44455                 }
44456                 
44457              };
44458         }
44459     }(),
44460     
44461     getAllAncestors: function()
44462     {
44463         var p = this.getSelectedNode();
44464         var a = [];
44465         if (!p) {
44466             a.push(p); // push blank onto stack..
44467             p = this.getParentElement();
44468         }
44469         
44470         
44471         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44472             a.push(p);
44473             p = p.parentNode;
44474         }
44475         a.push(this.doc.body);
44476         return a;
44477     },
44478     lastSel : false,
44479     lastSelNode : false,
44480     
44481     
44482     getSelection : function() 
44483     {
44484         this.assignDocWin();
44485         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44486     },
44487     
44488     getSelectedNode: function() 
44489     {
44490         // this may only work on Gecko!!!
44491         
44492         // should we cache this!!!!
44493         
44494         
44495         
44496          
44497         var range = this.createRange(this.getSelection()).cloneRange();
44498         
44499         if (Roo.isIE) {
44500             var parent = range.parentElement();
44501             while (true) {
44502                 var testRange = range.duplicate();
44503                 testRange.moveToElementText(parent);
44504                 if (testRange.inRange(range)) {
44505                     break;
44506                 }
44507                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44508                     break;
44509                 }
44510                 parent = parent.parentElement;
44511             }
44512             return parent;
44513         }
44514         
44515         // is ancestor a text element.
44516         var ac =  range.commonAncestorContainer;
44517         if (ac.nodeType == 3) {
44518             ac = ac.parentNode;
44519         }
44520         
44521         var ar = ac.childNodes;
44522          
44523         var nodes = [];
44524         var other_nodes = [];
44525         var has_other_nodes = false;
44526         for (var i=0;i<ar.length;i++) {
44527             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44528                 continue;
44529             }
44530             // fullly contained node.
44531             
44532             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44533                 nodes.push(ar[i]);
44534                 continue;
44535             }
44536             
44537             // probably selected..
44538             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44539                 other_nodes.push(ar[i]);
44540                 continue;
44541             }
44542             // outer..
44543             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44544                 continue;
44545             }
44546             
44547             
44548             has_other_nodes = true;
44549         }
44550         if (!nodes.length && other_nodes.length) {
44551             nodes= other_nodes;
44552         }
44553         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44554             return false;
44555         }
44556         
44557         return nodes[0];
44558     },
44559     createRange: function(sel)
44560     {
44561         // this has strange effects when using with 
44562         // top toolbar - not sure if it's a great idea.
44563         //this.editor.contentWindow.focus();
44564         if (typeof sel != "undefined") {
44565             try {
44566                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44567             } catch(e) {
44568                 return this.doc.createRange();
44569             }
44570         } else {
44571             return this.doc.createRange();
44572         }
44573     },
44574     getParentElement: function()
44575     {
44576         
44577         this.assignDocWin();
44578         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44579         
44580         var range = this.createRange(sel);
44581          
44582         try {
44583             var p = range.commonAncestorContainer;
44584             while (p.nodeType == 3) { // text node
44585                 p = p.parentNode;
44586             }
44587             return p;
44588         } catch (e) {
44589             return null;
44590         }
44591     
44592     },
44593     /***
44594      *
44595      * Range intersection.. the hard stuff...
44596      *  '-1' = before
44597      *  '0' = hits..
44598      *  '1' = after.
44599      *         [ -- selected range --- ]
44600      *   [fail]                        [fail]
44601      *
44602      *    basically..
44603      *      if end is before start or  hits it. fail.
44604      *      if start is after end or hits it fail.
44605      *
44606      *   if either hits (but other is outside. - then it's not 
44607      *   
44608      *    
44609      **/
44610     
44611     
44612     // @see http://www.thismuchiknow.co.uk/?p=64.
44613     rangeIntersectsNode : function(range, node)
44614     {
44615         var nodeRange = node.ownerDocument.createRange();
44616         try {
44617             nodeRange.selectNode(node);
44618         } catch (e) {
44619             nodeRange.selectNodeContents(node);
44620         }
44621     
44622         var rangeStartRange = range.cloneRange();
44623         rangeStartRange.collapse(true);
44624     
44625         var rangeEndRange = range.cloneRange();
44626         rangeEndRange.collapse(false);
44627     
44628         var nodeStartRange = nodeRange.cloneRange();
44629         nodeStartRange.collapse(true);
44630     
44631         var nodeEndRange = nodeRange.cloneRange();
44632         nodeEndRange.collapse(false);
44633     
44634         return rangeStartRange.compareBoundaryPoints(
44635                  Range.START_TO_START, nodeEndRange) == -1 &&
44636                rangeEndRange.compareBoundaryPoints(
44637                  Range.START_TO_START, nodeStartRange) == 1;
44638         
44639          
44640     },
44641     rangeCompareNode : function(range, node)
44642     {
44643         var nodeRange = node.ownerDocument.createRange();
44644         try {
44645             nodeRange.selectNode(node);
44646         } catch (e) {
44647             nodeRange.selectNodeContents(node);
44648         }
44649         
44650         
44651         range.collapse(true);
44652     
44653         nodeRange.collapse(true);
44654      
44655         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44656         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44657          
44658         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44659         
44660         var nodeIsBefore   =  ss == 1;
44661         var nodeIsAfter    = ee == -1;
44662         
44663         if (nodeIsBefore && nodeIsAfter) {
44664             return 0; // outer
44665         }
44666         if (!nodeIsBefore && nodeIsAfter) {
44667             return 1; //right trailed.
44668         }
44669         
44670         if (nodeIsBefore && !nodeIsAfter) {
44671             return 2;  // left trailed.
44672         }
44673         // fully contined.
44674         return 3;
44675     },
44676
44677     // private? - in a new class?
44678     cleanUpPaste :  function()
44679     {
44680         // cleans up the whole document..
44681         Roo.log('cleanuppaste');
44682         
44683         this.cleanUpChildren(this.doc.body);
44684         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44685         if (clean != this.doc.body.innerHTML) {
44686             this.doc.body.innerHTML = clean;
44687         }
44688         
44689     },
44690     
44691     cleanWordChars : function(input) {// change the chars to hex code
44692         var he = Roo.HtmlEditorCore;
44693         
44694         var output = input;
44695         Roo.each(he.swapCodes, function(sw) { 
44696             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44697             
44698             output = output.replace(swapper, sw[1]);
44699         });
44700         
44701         return output;
44702     },
44703     
44704     
44705     cleanUpChildren : function (n)
44706     {
44707         if (!n.childNodes.length) {
44708             return;
44709         }
44710         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44711            this.cleanUpChild(n.childNodes[i]);
44712         }
44713     },
44714     
44715     
44716         
44717     
44718     cleanUpChild : function (node)
44719     {
44720         var ed = this;
44721         //console.log(node);
44722         if (node.nodeName == "#text") {
44723             // clean up silly Windows -- stuff?
44724             return; 
44725         }
44726         if (node.nodeName == "#comment") {
44727             if (!this.allowComments) {
44728                 node.parentNode.removeChild(node);
44729             }
44730             // clean up silly Windows -- stuff?
44731             return; 
44732         }
44733         var lcname = node.tagName.toLowerCase();
44734         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44735         // whitelist of tags..
44736         
44737         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44738             // remove node.
44739             node.parentNode.removeChild(node);
44740             return;
44741             
44742         }
44743         
44744         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44745         
44746         // spans with no attributes - just remove them..
44747         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44748             remove_keep_children = true;
44749         }
44750         
44751         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44752         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44753         
44754         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44755         //    remove_keep_children = true;
44756         //}
44757         
44758         if (remove_keep_children) {
44759             this.cleanUpChildren(node);
44760             // inserts everything just before this node...
44761             while (node.childNodes.length) {
44762                 var cn = node.childNodes[0];
44763                 node.removeChild(cn);
44764                 node.parentNode.insertBefore(cn, node);
44765             }
44766             node.parentNode.removeChild(node);
44767             return;
44768         }
44769         
44770         if (!node.attributes || !node.attributes.length) {
44771             
44772           
44773             
44774             
44775             this.cleanUpChildren(node);
44776             return;
44777         }
44778         
44779         function cleanAttr(n,v)
44780         {
44781             
44782             if (v.match(/^\./) || v.match(/^\//)) {
44783                 return;
44784             }
44785             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44786                 return;
44787             }
44788             if (v.match(/^#/)) {
44789                 return;
44790             }
44791             if (v.match(/^\{/)) { // allow template editing.
44792                 return;
44793             }
44794 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44795             node.removeAttribute(n);
44796             
44797         }
44798         
44799         var cwhite = this.cwhite;
44800         var cblack = this.cblack;
44801             
44802         function cleanStyle(n,v)
44803         {
44804             if (v.match(/expression/)) { //XSS?? should we even bother..
44805                 node.removeAttribute(n);
44806                 return;
44807             }
44808             
44809             var parts = v.split(/;/);
44810             var clean = [];
44811             
44812             Roo.each(parts, function(p) {
44813                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44814                 if (!p.length) {
44815                     return true;
44816                 }
44817                 var l = p.split(':').shift().replace(/\s+/g,'');
44818                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44819                 
44820                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44821 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44822                     //node.removeAttribute(n);
44823                     return true;
44824                 }
44825                 //Roo.log()
44826                 // only allow 'c whitelisted system attributes'
44827                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44828 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44829                     //node.removeAttribute(n);
44830                     return true;
44831                 }
44832                 
44833                 
44834                  
44835                 
44836                 clean.push(p);
44837                 return true;
44838             });
44839             if (clean.length) { 
44840                 node.setAttribute(n, clean.join(';'));
44841             } else {
44842                 node.removeAttribute(n);
44843             }
44844             
44845         }
44846         
44847         
44848         for (var i = node.attributes.length-1; i > -1 ; i--) {
44849             var a = node.attributes[i];
44850             //console.log(a);
44851             
44852             if (a.name.toLowerCase().substr(0,2)=='on')  {
44853                 node.removeAttribute(a.name);
44854                 continue;
44855             }
44856             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44857                 node.removeAttribute(a.name);
44858                 continue;
44859             }
44860             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44861                 cleanAttr(a.name,a.value); // fixme..
44862                 continue;
44863             }
44864             if (a.name == 'style') {
44865                 cleanStyle(a.name,a.value);
44866                 continue;
44867             }
44868             /// clean up MS crap..
44869             // tecnically this should be a list of valid class'es..
44870             
44871             
44872             if (a.name == 'class') {
44873                 if (a.value.match(/^Mso/)) {
44874                     node.removeAttribute('class');
44875                 }
44876                 
44877                 if (a.value.match(/^body$/)) {
44878                     node.removeAttribute('class');
44879                 }
44880                 continue;
44881             }
44882             
44883             // style cleanup!?
44884             // class cleanup?
44885             
44886         }
44887         
44888         
44889         this.cleanUpChildren(node);
44890         
44891         
44892     },
44893     
44894     /**
44895      * Clean up MS wordisms...
44896      */
44897     cleanWord : function(node)
44898     {
44899         if (!node) {
44900             this.cleanWord(this.doc.body);
44901             return;
44902         }
44903         
44904         if(
44905                 node.nodeName == 'SPAN' &&
44906                 !node.hasAttributes() &&
44907                 node.childNodes.length == 1 &&
44908                 node.firstChild.nodeName == "#text"  
44909         ) {
44910             var textNode = node.firstChild;
44911             node.removeChild(textNode);
44912             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44913                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44914             }
44915             node.parentNode.insertBefore(textNode, node);
44916             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44917                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44918             }
44919             node.parentNode.removeChild(node);
44920         }
44921         
44922         if (node.nodeName == "#text") {
44923             // clean up silly Windows -- stuff?
44924             return; 
44925         }
44926         if (node.nodeName == "#comment") {
44927             node.parentNode.removeChild(node);
44928             // clean up silly Windows -- stuff?
44929             return; 
44930         }
44931         
44932         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44933             node.parentNode.removeChild(node);
44934             return;
44935         }
44936         //Roo.log(node.tagName);
44937         // remove - but keep children..
44938         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44939             //Roo.log('-- removed');
44940             while (node.childNodes.length) {
44941                 var cn = node.childNodes[0];
44942                 node.removeChild(cn);
44943                 node.parentNode.insertBefore(cn, node);
44944                 // move node to parent - and clean it..
44945                 this.cleanWord(cn);
44946             }
44947             node.parentNode.removeChild(node);
44948             /// no need to iterate chidlren = it's got none..
44949             //this.iterateChildren(node, this.cleanWord);
44950             return;
44951         }
44952         // clean styles
44953         if (node.className.length) {
44954             
44955             var cn = node.className.split(/\W+/);
44956             var cna = [];
44957             Roo.each(cn, function(cls) {
44958                 if (cls.match(/Mso[a-zA-Z]+/)) {
44959                     return;
44960                 }
44961                 cna.push(cls);
44962             });
44963             node.className = cna.length ? cna.join(' ') : '';
44964             if (!cna.length) {
44965                 node.removeAttribute("class");
44966             }
44967         }
44968         
44969         if (node.hasAttribute("lang")) {
44970             node.removeAttribute("lang");
44971         }
44972         
44973         if (node.hasAttribute("style")) {
44974             
44975             var styles = node.getAttribute("style").split(";");
44976             var nstyle = [];
44977             Roo.each(styles, function(s) {
44978                 if (!s.match(/:/)) {
44979                     return;
44980                 }
44981                 var kv = s.split(":");
44982                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44983                     return;
44984                 }
44985                 // what ever is left... we allow.
44986                 nstyle.push(s);
44987             });
44988             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44989             if (!nstyle.length) {
44990                 node.removeAttribute('style');
44991             }
44992         }
44993         this.iterateChildren(node, this.cleanWord);
44994         
44995         
44996         
44997     },
44998     /**
44999      * iterateChildren of a Node, calling fn each time, using this as the scole..
45000      * @param {DomNode} node node to iterate children of.
45001      * @param {Function} fn method of this class to call on each item.
45002      */
45003     iterateChildren : function(node, fn)
45004     {
45005         if (!node.childNodes.length) {
45006                 return;
45007         }
45008         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45009            fn.call(this, node.childNodes[i])
45010         }
45011     },
45012     
45013     
45014     /**
45015      * cleanTableWidths.
45016      *
45017      * Quite often pasting from word etc.. results in tables with column and widths.
45018      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45019      *
45020      */
45021     cleanTableWidths : function(node)
45022     {
45023          
45024          
45025         if (!node) {
45026             this.cleanTableWidths(this.doc.body);
45027             return;
45028         }
45029         
45030         // ignore list...
45031         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45032             return; 
45033         }
45034         Roo.log(node.tagName);
45035         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45036             this.iterateChildren(node, this.cleanTableWidths);
45037             return;
45038         }
45039         if (node.hasAttribute('width')) {
45040             node.removeAttribute('width');
45041         }
45042         
45043          
45044         if (node.hasAttribute("style")) {
45045             // pretty basic...
45046             
45047             var styles = node.getAttribute("style").split(";");
45048             var nstyle = [];
45049             Roo.each(styles, function(s) {
45050                 if (!s.match(/:/)) {
45051                     return;
45052                 }
45053                 var kv = s.split(":");
45054                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45055                     return;
45056                 }
45057                 // what ever is left... we allow.
45058                 nstyle.push(s);
45059             });
45060             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45061             if (!nstyle.length) {
45062                 node.removeAttribute('style');
45063             }
45064         }
45065         
45066         this.iterateChildren(node, this.cleanTableWidths);
45067         
45068         
45069     },
45070     
45071     
45072     
45073     
45074     domToHTML : function(currentElement, depth, nopadtext) {
45075         
45076         depth = depth || 0;
45077         nopadtext = nopadtext || false;
45078     
45079         if (!currentElement) {
45080             return this.domToHTML(this.doc.body);
45081         }
45082         
45083         //Roo.log(currentElement);
45084         var j;
45085         var allText = false;
45086         var nodeName = currentElement.nodeName;
45087         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45088         
45089         if  (nodeName == '#text') {
45090             
45091             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45092         }
45093         
45094         
45095         var ret = '';
45096         if (nodeName != 'BODY') {
45097              
45098             var i = 0;
45099             // Prints the node tagName, such as <A>, <IMG>, etc
45100             if (tagName) {
45101                 var attr = [];
45102                 for(i = 0; i < currentElement.attributes.length;i++) {
45103                     // quoting?
45104                     var aname = currentElement.attributes.item(i).name;
45105                     if (!currentElement.attributes.item(i).value.length) {
45106                         continue;
45107                     }
45108                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45109                 }
45110                 
45111                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45112             } 
45113             else {
45114                 
45115                 // eack
45116             }
45117         } else {
45118             tagName = false;
45119         }
45120         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45121             return ret;
45122         }
45123         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45124             nopadtext = true;
45125         }
45126         
45127         
45128         // Traverse the tree
45129         i = 0;
45130         var currentElementChild = currentElement.childNodes.item(i);
45131         var allText = true;
45132         var innerHTML  = '';
45133         lastnode = '';
45134         while (currentElementChild) {
45135             // Formatting code (indent the tree so it looks nice on the screen)
45136             var nopad = nopadtext;
45137             if (lastnode == 'SPAN') {
45138                 nopad  = true;
45139             }
45140             // text
45141             if  (currentElementChild.nodeName == '#text') {
45142                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45143                 toadd = nopadtext ? toadd : toadd.trim();
45144                 if (!nopad && toadd.length > 80) {
45145                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45146                 }
45147                 innerHTML  += toadd;
45148                 
45149                 i++;
45150                 currentElementChild = currentElement.childNodes.item(i);
45151                 lastNode = '';
45152                 continue;
45153             }
45154             allText = false;
45155             
45156             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45157                 
45158             // Recursively traverse the tree structure of the child node
45159             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45160             lastnode = currentElementChild.nodeName;
45161             i++;
45162             currentElementChild=currentElement.childNodes.item(i);
45163         }
45164         
45165         ret += innerHTML;
45166         
45167         if (!allText) {
45168                 // The remaining code is mostly for formatting the tree
45169             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45170         }
45171         
45172         
45173         if (tagName) {
45174             ret+= "</"+tagName+">";
45175         }
45176         return ret;
45177         
45178     },
45179         
45180     applyBlacklists : function()
45181     {
45182         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45183         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45184         
45185         this.white = [];
45186         this.black = [];
45187         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45188             if (b.indexOf(tag) > -1) {
45189                 return;
45190             }
45191             this.white.push(tag);
45192             
45193         }, this);
45194         
45195         Roo.each(w, function(tag) {
45196             if (b.indexOf(tag) > -1) {
45197                 return;
45198             }
45199             if (this.white.indexOf(tag) > -1) {
45200                 return;
45201             }
45202             this.white.push(tag);
45203             
45204         }, this);
45205         
45206         
45207         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45208             if (w.indexOf(tag) > -1) {
45209                 return;
45210             }
45211             this.black.push(tag);
45212             
45213         }, this);
45214         
45215         Roo.each(b, function(tag) {
45216             if (w.indexOf(tag) > -1) {
45217                 return;
45218             }
45219             if (this.black.indexOf(tag) > -1) {
45220                 return;
45221             }
45222             this.black.push(tag);
45223             
45224         }, this);
45225         
45226         
45227         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45228         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45229         
45230         this.cwhite = [];
45231         this.cblack = [];
45232         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45233             if (b.indexOf(tag) > -1) {
45234                 return;
45235             }
45236             this.cwhite.push(tag);
45237             
45238         }, this);
45239         
45240         Roo.each(w, function(tag) {
45241             if (b.indexOf(tag) > -1) {
45242                 return;
45243             }
45244             if (this.cwhite.indexOf(tag) > -1) {
45245                 return;
45246             }
45247             this.cwhite.push(tag);
45248             
45249         }, this);
45250         
45251         
45252         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45253             if (w.indexOf(tag) > -1) {
45254                 return;
45255             }
45256             this.cblack.push(tag);
45257             
45258         }, this);
45259         
45260         Roo.each(b, function(tag) {
45261             if (w.indexOf(tag) > -1) {
45262                 return;
45263             }
45264             if (this.cblack.indexOf(tag) > -1) {
45265                 return;
45266             }
45267             this.cblack.push(tag);
45268             
45269         }, this);
45270     },
45271     
45272     setStylesheets : function(stylesheets)
45273     {
45274         if(typeof(stylesheets) == 'string'){
45275             Roo.get(this.iframe.contentDocument.head).createChild({
45276                 tag : 'link',
45277                 rel : 'stylesheet',
45278                 type : 'text/css',
45279                 href : stylesheets
45280             });
45281             
45282             return;
45283         }
45284         var _this = this;
45285      
45286         Roo.each(stylesheets, function(s) {
45287             if(!s.length){
45288                 return;
45289             }
45290             
45291             Roo.get(_this.iframe.contentDocument.head).createChild({
45292                 tag : 'link',
45293                 rel : 'stylesheet',
45294                 type : 'text/css',
45295                 href : s
45296             });
45297         });
45298
45299         
45300     },
45301     
45302     removeStylesheets : function()
45303     {
45304         var _this = this;
45305         
45306         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45307             s.remove();
45308         });
45309     },
45310     
45311     setStyle : function(style)
45312     {
45313         Roo.get(this.iframe.contentDocument.head).createChild({
45314             tag : 'style',
45315             type : 'text/css',
45316             html : style
45317         });
45318
45319         return;
45320     }
45321     
45322     // hide stuff that is not compatible
45323     /**
45324      * @event blur
45325      * @hide
45326      */
45327     /**
45328      * @event change
45329      * @hide
45330      */
45331     /**
45332      * @event focus
45333      * @hide
45334      */
45335     /**
45336      * @event specialkey
45337      * @hide
45338      */
45339     /**
45340      * @cfg {String} fieldClass @hide
45341      */
45342     /**
45343      * @cfg {String} focusClass @hide
45344      */
45345     /**
45346      * @cfg {String} autoCreate @hide
45347      */
45348     /**
45349      * @cfg {String} inputType @hide
45350      */
45351     /**
45352      * @cfg {String} invalidClass @hide
45353      */
45354     /**
45355      * @cfg {String} invalidText @hide
45356      */
45357     /**
45358      * @cfg {String} msgFx @hide
45359      */
45360     /**
45361      * @cfg {String} validateOnBlur @hide
45362      */
45363 });
45364
45365 Roo.HtmlEditorCore.white = [
45366         'area', 'br', 'img', 'input', 'hr', 'wbr',
45367         
45368        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45369        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45370        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45371        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45372        'table',   'ul',         'xmp', 
45373        
45374        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45375       'thead',   'tr', 
45376      
45377       'dir', 'menu', 'ol', 'ul', 'dl',
45378        
45379       'embed',  'object'
45380 ];
45381
45382
45383 Roo.HtmlEditorCore.black = [
45384     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45385         'applet', // 
45386         'base',   'basefont', 'bgsound', 'blink',  'body', 
45387         'frame',  'frameset', 'head',    'html',   'ilayer', 
45388         'iframe', 'layer',  'link',     'meta',    'object',   
45389         'script', 'style' ,'title',  'xml' // clean later..
45390 ];
45391 Roo.HtmlEditorCore.clean = [
45392     'script', 'style', 'title', 'xml'
45393 ];
45394 Roo.HtmlEditorCore.remove = [
45395     'font'
45396 ];
45397 // attributes..
45398
45399 Roo.HtmlEditorCore.ablack = [
45400     'on'
45401 ];
45402     
45403 Roo.HtmlEditorCore.aclean = [ 
45404     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45405 ];
45406
45407 // protocols..
45408 Roo.HtmlEditorCore.pwhite= [
45409         'http',  'https',  'mailto'
45410 ];
45411
45412 // white listed style attributes.
45413 Roo.HtmlEditorCore.cwhite= [
45414       //  'text-align', /// default is to allow most things..
45415       
45416          
45417 //        'font-size'//??
45418 ];
45419
45420 // black listed style attributes.
45421 Roo.HtmlEditorCore.cblack= [
45422       //  'font-size' -- this can be set by the project 
45423 ];
45424
45425
45426 Roo.HtmlEditorCore.swapCodes   =[ 
45427     [    8211, "&#8211;" ], 
45428     [    8212, "&#8212;" ], 
45429     [    8216,  "'" ],  
45430     [    8217, "'" ],  
45431     [    8220, '"' ],  
45432     [    8221, '"' ],  
45433     [    8226, "*" ],  
45434     [    8230, "..." ]
45435 ]; 
45436
45437     //<script type="text/javascript">
45438
45439 /*
45440  * Ext JS Library 1.1.1
45441  * Copyright(c) 2006-2007, Ext JS, LLC.
45442  * Licence LGPL
45443  * 
45444  */
45445  
45446  
45447 Roo.form.HtmlEditor = function(config){
45448     
45449     
45450     
45451     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45452     
45453     if (!this.toolbars) {
45454         this.toolbars = [];
45455     }
45456     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45457     
45458     
45459 };
45460
45461 /**
45462  * @class Roo.form.HtmlEditor
45463  * @extends Roo.form.Field
45464  * Provides a lightweight HTML Editor component.
45465  *
45466  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45467  * 
45468  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45469  * supported by this editor.</b><br/><br/>
45470  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45471  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45472  */
45473 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45474     /**
45475      * @cfg {Boolean} clearUp
45476      */
45477     clearUp : true,
45478       /**
45479      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45480      */
45481     toolbars : false,
45482    
45483      /**
45484      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45485      *                        Roo.resizable.
45486      */
45487     resizable : false,
45488      /**
45489      * @cfg {Number} height (in pixels)
45490      */   
45491     height: 300,
45492    /**
45493      * @cfg {Number} width (in pixels)
45494      */   
45495     width: 500,
45496     
45497     /**
45498      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45499      * 
45500      */
45501     stylesheets: false,
45502     
45503     
45504      /**
45505      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45506      * 
45507      */
45508     cblack: false,
45509     /**
45510      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45511      * 
45512      */
45513     cwhite: false,
45514     
45515      /**
45516      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45517      * 
45518      */
45519     black: false,
45520     /**
45521      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45522      * 
45523      */
45524     white: false,
45525     /**
45526      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45527      */
45528     allowComments: false,
45529     
45530     // id of frame..
45531     frameId: false,
45532     
45533     // private properties
45534     validationEvent : false,
45535     deferHeight: true,
45536     initialized : false,
45537     activated : false,
45538     
45539     onFocus : Roo.emptyFn,
45540     iframePad:3,
45541     hideMode:'offsets',
45542     
45543     actionMode : 'container', // defaults to hiding it...
45544     
45545     defaultAutoCreate : { // modified by initCompnoent..
45546         tag: "textarea",
45547         style:"width:500px;height:300px;",
45548         autocomplete: "new-password"
45549     },
45550
45551     // private
45552     initComponent : function(){
45553         this.addEvents({
45554             /**
45555              * @event initialize
45556              * Fires when the editor is fully initialized (including the iframe)
45557              * @param {HtmlEditor} this
45558              */
45559             initialize: true,
45560             /**
45561              * @event activate
45562              * Fires when the editor is first receives the focus. Any insertion must wait
45563              * until after this event.
45564              * @param {HtmlEditor} this
45565              */
45566             activate: true,
45567              /**
45568              * @event beforesync
45569              * Fires before the textarea is updated with content from the editor iframe. Return false
45570              * to cancel the sync.
45571              * @param {HtmlEditor} this
45572              * @param {String} html
45573              */
45574             beforesync: true,
45575              /**
45576              * @event beforepush
45577              * Fires before the iframe editor is updated with content from the textarea. Return false
45578              * to cancel the push.
45579              * @param {HtmlEditor} this
45580              * @param {String} html
45581              */
45582             beforepush: true,
45583              /**
45584              * @event sync
45585              * Fires when the textarea is updated with content from the editor iframe.
45586              * @param {HtmlEditor} this
45587              * @param {String} html
45588              */
45589             sync: true,
45590              /**
45591              * @event push
45592              * Fires when the iframe editor is updated with content from the textarea.
45593              * @param {HtmlEditor} this
45594              * @param {String} html
45595              */
45596             push: true,
45597              /**
45598              * @event editmodechange
45599              * Fires when the editor switches edit modes
45600              * @param {HtmlEditor} this
45601              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45602              */
45603             editmodechange: true,
45604             /**
45605              * @event editorevent
45606              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45607              * @param {HtmlEditor} this
45608              */
45609             editorevent: true,
45610             /**
45611              * @event firstfocus
45612              * Fires when on first focus - needed by toolbars..
45613              * @param {HtmlEditor} this
45614              */
45615             firstfocus: true,
45616             /**
45617              * @event autosave
45618              * Auto save the htmlEditor value as a file into Events
45619              * @param {HtmlEditor} this
45620              */
45621             autosave: true,
45622             /**
45623              * @event savedpreview
45624              * preview the saved version of htmlEditor
45625              * @param {HtmlEditor} this
45626              */
45627             savedpreview: true,
45628             
45629             /**
45630             * @event stylesheetsclick
45631             * Fires when press the Sytlesheets button
45632             * @param {Roo.HtmlEditorCore} this
45633             */
45634             stylesheetsclick: true
45635         });
45636         this.defaultAutoCreate =  {
45637             tag: "textarea",
45638             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45639             autocomplete: "new-password"
45640         };
45641     },
45642
45643     /**
45644      * Protected method that will not generally be called directly. It
45645      * is called when the editor creates its toolbar. Override this method if you need to
45646      * add custom toolbar buttons.
45647      * @param {HtmlEditor} editor
45648      */
45649     createToolbar : function(editor){
45650         Roo.log("create toolbars");
45651         if (!editor.toolbars || !editor.toolbars.length) {
45652             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45653         }
45654         
45655         for (var i =0 ; i < editor.toolbars.length;i++) {
45656             editor.toolbars[i] = Roo.factory(
45657                     typeof(editor.toolbars[i]) == 'string' ?
45658                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45659                 Roo.form.HtmlEditor);
45660             editor.toolbars[i].init(editor);
45661         }
45662          
45663         
45664     },
45665
45666      
45667     // private
45668     onRender : function(ct, position)
45669     {
45670         var _t = this;
45671         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45672         
45673         this.wrap = this.el.wrap({
45674             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45675         });
45676         
45677         this.editorcore.onRender(ct, position);
45678          
45679         if (this.resizable) {
45680             this.resizeEl = new Roo.Resizable(this.wrap, {
45681                 pinned : true,
45682                 wrap: true,
45683                 dynamic : true,
45684                 minHeight : this.height,
45685                 height: this.height,
45686                 handles : this.resizable,
45687                 width: this.width,
45688                 listeners : {
45689                     resize : function(r, w, h) {
45690                         _t.onResize(w,h); // -something
45691                     }
45692                 }
45693             });
45694             
45695         }
45696         this.createToolbar(this);
45697        
45698         
45699         if(!this.width){
45700             this.setSize(this.wrap.getSize());
45701         }
45702         if (this.resizeEl) {
45703             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45704             // should trigger onReize..
45705         }
45706         
45707         this.keyNav = new Roo.KeyNav(this.el, {
45708             
45709             "tab" : function(e){
45710                 e.preventDefault();
45711                 
45712                 var value = this.getValue();
45713                 
45714                 var start = this.el.dom.selectionStart;
45715                 var end = this.el.dom.selectionEnd;
45716                 
45717                 if(!e.shiftKey){
45718                     
45719                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45720                     this.el.dom.setSelectionRange(end + 1, end + 1);
45721                     return;
45722                 }
45723                 
45724                 var f = value.substring(0, start).split("\t");
45725                 
45726                 if(f.pop().length != 0){
45727                     return;
45728                 }
45729                 
45730                 this.setValue(f.join("\t") + value.substring(end));
45731                 this.el.dom.setSelectionRange(start - 1, start - 1);
45732                 
45733             },
45734             
45735             "home" : function(e){
45736                 e.preventDefault();
45737                 
45738                 var curr = this.el.dom.selectionStart;
45739                 var lines = this.getValue().split("\n");
45740                 
45741                 if(!lines.length){
45742                     return;
45743                 }
45744                 
45745                 if(e.ctrlKey){
45746                     this.el.dom.setSelectionRange(0, 0);
45747                     return;
45748                 }
45749                 
45750                 var pos = 0;
45751                 
45752                 for (var i = 0; i < lines.length;i++) {
45753                     pos += lines[i].length;
45754                     
45755                     if(i != 0){
45756                         pos += 1;
45757                     }
45758                     
45759                     if(pos < curr){
45760                         continue;
45761                     }
45762                     
45763                     pos -= lines[i].length;
45764                     
45765                     break;
45766                 }
45767                 
45768                 if(!e.shiftKey){
45769                     this.el.dom.setSelectionRange(pos, pos);
45770                     return;
45771                 }
45772                 
45773                 this.el.dom.selectionStart = pos;
45774                 this.el.dom.selectionEnd = curr;
45775             },
45776             
45777             "end" : function(e){
45778                 e.preventDefault();
45779                 
45780                 var curr = this.el.dom.selectionStart;
45781                 var lines = this.getValue().split("\n");
45782                 
45783                 if(!lines.length){
45784                     return;
45785                 }
45786                 
45787                 if(e.ctrlKey){
45788                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45789                     return;
45790                 }
45791                 
45792                 var pos = 0;
45793                 
45794                 for (var i = 0; i < lines.length;i++) {
45795                     
45796                     pos += lines[i].length;
45797                     
45798                     if(i != 0){
45799                         pos += 1;
45800                     }
45801                     
45802                     if(pos < curr){
45803                         continue;
45804                     }
45805                     
45806                     break;
45807                 }
45808                 
45809                 if(!e.shiftKey){
45810                     this.el.dom.setSelectionRange(pos, pos);
45811                     return;
45812                 }
45813                 
45814                 this.el.dom.selectionStart = curr;
45815                 this.el.dom.selectionEnd = pos;
45816             },
45817
45818             scope : this,
45819
45820             doRelay : function(foo, bar, hname){
45821                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45822             },
45823
45824             forceKeyDown: true
45825         });
45826         
45827 //        if(this.autosave && this.w){
45828 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45829 //        }
45830     },
45831
45832     // private
45833     onResize : function(w, h)
45834     {
45835         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45836         var ew = false;
45837         var eh = false;
45838         
45839         if(this.el ){
45840             if(typeof w == 'number'){
45841                 var aw = w - this.wrap.getFrameWidth('lr');
45842                 this.el.setWidth(this.adjustWidth('textarea', aw));
45843                 ew = aw;
45844             }
45845             if(typeof h == 'number'){
45846                 var tbh = 0;
45847                 for (var i =0; i < this.toolbars.length;i++) {
45848                     // fixme - ask toolbars for heights?
45849                     tbh += this.toolbars[i].tb.el.getHeight();
45850                     if (this.toolbars[i].footer) {
45851                         tbh += this.toolbars[i].footer.el.getHeight();
45852                     }
45853                 }
45854                 
45855                 
45856                 
45857                 
45858                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45859                 ah -= 5; // knock a few pixes off for look..
45860 //                Roo.log(ah);
45861                 this.el.setHeight(this.adjustWidth('textarea', ah));
45862                 var eh = ah;
45863             }
45864         }
45865         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45866         this.editorcore.onResize(ew,eh);
45867         
45868     },
45869
45870     /**
45871      * Toggles the editor between standard and source edit mode.
45872      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45873      */
45874     toggleSourceEdit : function(sourceEditMode)
45875     {
45876         this.editorcore.toggleSourceEdit(sourceEditMode);
45877         
45878         if(this.editorcore.sourceEditMode){
45879             Roo.log('editor - showing textarea');
45880             
45881 //            Roo.log('in');
45882 //            Roo.log(this.syncValue());
45883             this.editorcore.syncValue();
45884             this.el.removeClass('x-hidden');
45885             this.el.dom.removeAttribute('tabIndex');
45886             this.el.focus();
45887             
45888             for (var i = 0; i < this.toolbars.length; i++) {
45889                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45890                     this.toolbars[i].tb.hide();
45891                     this.toolbars[i].footer.hide();
45892                 }
45893             }
45894             
45895         }else{
45896             Roo.log('editor - hiding textarea');
45897 //            Roo.log('out')
45898 //            Roo.log(this.pushValue()); 
45899             this.editorcore.pushValue();
45900             
45901             this.el.addClass('x-hidden');
45902             this.el.dom.setAttribute('tabIndex', -1);
45903             
45904             for (var i = 0; i < this.toolbars.length; i++) {
45905                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45906                     this.toolbars[i].tb.show();
45907                     this.toolbars[i].footer.show();
45908                 }
45909             }
45910             
45911             //this.deferFocus();
45912         }
45913         
45914         this.setSize(this.wrap.getSize());
45915         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45916         
45917         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45918     },
45919  
45920     // private (for BoxComponent)
45921     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45922
45923     // private (for BoxComponent)
45924     getResizeEl : function(){
45925         return this.wrap;
45926     },
45927
45928     // private (for BoxComponent)
45929     getPositionEl : function(){
45930         return this.wrap;
45931     },
45932
45933     // private
45934     initEvents : function(){
45935         this.originalValue = this.getValue();
45936     },
45937
45938     /**
45939      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45940      * @method
45941      */
45942     markInvalid : Roo.emptyFn,
45943     /**
45944      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45945      * @method
45946      */
45947     clearInvalid : Roo.emptyFn,
45948
45949     setValue : function(v){
45950         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45951         this.editorcore.pushValue();
45952     },
45953
45954      
45955     // private
45956     deferFocus : function(){
45957         this.focus.defer(10, this);
45958     },
45959
45960     // doc'ed in Field
45961     focus : function(){
45962         this.editorcore.focus();
45963         
45964     },
45965       
45966
45967     // private
45968     onDestroy : function(){
45969         
45970         
45971         
45972         if(this.rendered){
45973             
45974             for (var i =0; i < this.toolbars.length;i++) {
45975                 // fixme - ask toolbars for heights?
45976                 this.toolbars[i].onDestroy();
45977             }
45978             
45979             this.wrap.dom.innerHTML = '';
45980             this.wrap.remove();
45981         }
45982     },
45983
45984     // private
45985     onFirstFocus : function(){
45986         //Roo.log("onFirstFocus");
45987         this.editorcore.onFirstFocus();
45988          for (var i =0; i < this.toolbars.length;i++) {
45989             this.toolbars[i].onFirstFocus();
45990         }
45991         
45992     },
45993     
45994     // private
45995     syncValue : function()
45996     {
45997         this.editorcore.syncValue();
45998     },
45999     
46000     pushValue : function()
46001     {
46002         this.editorcore.pushValue();
46003     },
46004     
46005     setStylesheets : function(stylesheets)
46006     {
46007         this.editorcore.setStylesheets(stylesheets);
46008     },
46009     
46010     removeStylesheets : function()
46011     {
46012         this.editorcore.removeStylesheets();
46013     }
46014      
46015     
46016     // hide stuff that is not compatible
46017     /**
46018      * @event blur
46019      * @hide
46020      */
46021     /**
46022      * @event change
46023      * @hide
46024      */
46025     /**
46026      * @event focus
46027      * @hide
46028      */
46029     /**
46030      * @event specialkey
46031      * @hide
46032      */
46033     /**
46034      * @cfg {String} fieldClass @hide
46035      */
46036     /**
46037      * @cfg {String} focusClass @hide
46038      */
46039     /**
46040      * @cfg {String} autoCreate @hide
46041      */
46042     /**
46043      * @cfg {String} inputType @hide
46044      */
46045     /**
46046      * @cfg {String} invalidClass @hide
46047      */
46048     /**
46049      * @cfg {String} invalidText @hide
46050      */
46051     /**
46052      * @cfg {String} msgFx @hide
46053      */
46054     /**
46055      * @cfg {String} validateOnBlur @hide
46056      */
46057 });
46058  
46059     // <script type="text/javascript">
46060 /*
46061  * Based on
46062  * Ext JS Library 1.1.1
46063  * Copyright(c) 2006-2007, Ext JS, LLC.
46064  *  
46065  
46066  */
46067
46068 /**
46069  * @class Roo.form.HtmlEditorToolbar1
46070  * Basic Toolbar
46071  * 
46072  * Usage:
46073  *
46074  new Roo.form.HtmlEditor({
46075     ....
46076     toolbars : [
46077         new Roo.form.HtmlEditorToolbar1({
46078             disable : { fonts: 1 , format: 1, ..., ... , ...],
46079             btns : [ .... ]
46080         })
46081     }
46082      
46083  * 
46084  * @cfg {Object} disable List of elements to disable..
46085  * @cfg {Array} btns List of additional buttons.
46086  * 
46087  * 
46088  * NEEDS Extra CSS? 
46089  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46090  */
46091  
46092 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46093 {
46094     
46095     Roo.apply(this, config);
46096     
46097     // default disabled, based on 'good practice'..
46098     this.disable = this.disable || {};
46099     Roo.applyIf(this.disable, {
46100         fontSize : true,
46101         colors : true,
46102         specialElements : true
46103     });
46104     
46105     
46106     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46107     // dont call parent... till later.
46108 }
46109
46110 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46111     
46112     tb: false,
46113     
46114     rendered: false,
46115     
46116     editor : false,
46117     editorcore : false,
46118     /**
46119      * @cfg {Object} disable  List of toolbar elements to disable
46120          
46121      */
46122     disable : false,
46123     
46124     
46125      /**
46126      * @cfg {String} createLinkText The default text for the create link prompt
46127      */
46128     createLinkText : 'Please enter the URL for the link:',
46129     /**
46130      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46131      */
46132     defaultLinkValue : 'http:/'+'/',
46133    
46134     
46135       /**
46136      * @cfg {Array} fontFamilies An array of available font families
46137      */
46138     fontFamilies : [
46139         'Arial',
46140         'Courier New',
46141         'Tahoma',
46142         'Times New Roman',
46143         'Verdana'
46144     ],
46145     
46146     specialChars : [
46147            "&#169;",
46148           "&#174;",     
46149           "&#8482;",    
46150           "&#163;" ,    
46151          // "&#8212;",    
46152           "&#8230;",    
46153           "&#247;" ,    
46154         //  "&#225;" ,     ?? a acute?
46155            "&#8364;"    , //Euro
46156        //   "&#8220;"    ,
46157         //  "&#8221;"    ,
46158         //  "&#8226;"    ,
46159           "&#176;"  //   , // degrees
46160
46161          // "&#233;"     , // e ecute
46162          // "&#250;"     , // u ecute?
46163     ],
46164     
46165     specialElements : [
46166         {
46167             text: "Insert Table",
46168             xtype: 'MenuItem',
46169             xns : Roo.Menu,
46170             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46171                 
46172         },
46173         {    
46174             text: "Insert Image",
46175             xtype: 'MenuItem',
46176             xns : Roo.Menu,
46177             ihtml : '<img src="about:blank"/>'
46178             
46179         }
46180         
46181          
46182     ],
46183     
46184     
46185     inputElements : [ 
46186             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46187             "input:submit", "input:button", "select", "textarea", "label" ],
46188     formats : [
46189         ["p"] ,  
46190         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46191         ["pre"],[ "code"], 
46192         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46193         ['div'],['span'],
46194         ['sup'],['sub']
46195     ],
46196     
46197     cleanStyles : [
46198         "font-size"
46199     ],
46200      /**
46201      * @cfg {String} defaultFont default font to use.
46202      */
46203     defaultFont: 'tahoma',
46204    
46205     fontSelect : false,
46206     
46207     
46208     formatCombo : false,
46209     
46210     init : function(editor)
46211     {
46212         this.editor = editor;
46213         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46214         var editorcore = this.editorcore;
46215         
46216         var _t = this;
46217         
46218         var fid = editorcore.frameId;
46219         var etb = this;
46220         function btn(id, toggle, handler){
46221             var xid = fid + '-'+ id ;
46222             return {
46223                 id : xid,
46224                 cmd : id,
46225                 cls : 'x-btn-icon x-edit-'+id,
46226                 enableToggle:toggle !== false,
46227                 scope: _t, // was editor...
46228                 handler:handler||_t.relayBtnCmd,
46229                 clickEvent:'mousedown',
46230                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46231                 tabIndex:-1
46232             };
46233         }
46234         
46235         
46236         
46237         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46238         this.tb = tb;
46239          // stop form submits
46240         tb.el.on('click', function(e){
46241             e.preventDefault(); // what does this do?
46242         });
46243
46244         if(!this.disable.font) { // && !Roo.isSafari){
46245             /* why no safari for fonts 
46246             editor.fontSelect = tb.el.createChild({
46247                 tag:'select',
46248                 tabIndex: -1,
46249                 cls:'x-font-select',
46250                 html: this.createFontOptions()
46251             });
46252             
46253             editor.fontSelect.on('change', function(){
46254                 var font = editor.fontSelect.dom.value;
46255                 editor.relayCmd('fontname', font);
46256                 editor.deferFocus();
46257             }, editor);
46258             
46259             tb.add(
46260                 editor.fontSelect.dom,
46261                 '-'
46262             );
46263             */
46264             
46265         };
46266         if(!this.disable.formats){
46267             this.formatCombo = new Roo.form.ComboBox({
46268                 store: new Roo.data.SimpleStore({
46269                     id : 'tag',
46270                     fields: ['tag'],
46271                     data : this.formats // from states.js
46272                 }),
46273                 blockFocus : true,
46274                 name : '',
46275                 //autoCreate : {tag: "div",  size: "20"},
46276                 displayField:'tag',
46277                 typeAhead: false,
46278                 mode: 'local',
46279                 editable : false,
46280                 triggerAction: 'all',
46281                 emptyText:'Add tag',
46282                 selectOnFocus:true,
46283                 width:135,
46284                 listeners : {
46285                     'select': function(c, r, i) {
46286                         editorcore.insertTag(r.get('tag'));
46287                         editor.focus();
46288                     }
46289                 }
46290
46291             });
46292             tb.addField(this.formatCombo);
46293             
46294         }
46295         
46296         if(!this.disable.format){
46297             tb.add(
46298                 btn('bold'),
46299                 btn('italic'),
46300                 btn('underline'),
46301                 btn('strikethrough')
46302             );
46303         };
46304         if(!this.disable.fontSize){
46305             tb.add(
46306                 '-',
46307                 
46308                 
46309                 btn('increasefontsize', false, editorcore.adjustFont),
46310                 btn('decreasefontsize', false, editorcore.adjustFont)
46311             );
46312         };
46313         
46314         
46315         if(!this.disable.colors){
46316             tb.add(
46317                 '-', {
46318                     id:editorcore.frameId +'-forecolor',
46319                     cls:'x-btn-icon x-edit-forecolor',
46320                     clickEvent:'mousedown',
46321                     tooltip: this.buttonTips['forecolor'] || undefined,
46322                     tabIndex:-1,
46323                     menu : new Roo.menu.ColorMenu({
46324                         allowReselect: true,
46325                         focus: Roo.emptyFn,
46326                         value:'000000',
46327                         plain:true,
46328                         selectHandler: function(cp, color){
46329                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46330                             editor.deferFocus();
46331                         },
46332                         scope: editorcore,
46333                         clickEvent:'mousedown'
46334                     })
46335                 }, {
46336                     id:editorcore.frameId +'backcolor',
46337                     cls:'x-btn-icon x-edit-backcolor',
46338                     clickEvent:'mousedown',
46339                     tooltip: this.buttonTips['backcolor'] || undefined,
46340                     tabIndex:-1,
46341                     menu : new Roo.menu.ColorMenu({
46342                         focus: Roo.emptyFn,
46343                         value:'FFFFFF',
46344                         plain:true,
46345                         allowReselect: true,
46346                         selectHandler: function(cp, color){
46347                             if(Roo.isGecko){
46348                                 editorcore.execCmd('useCSS', false);
46349                                 editorcore.execCmd('hilitecolor', color);
46350                                 editorcore.execCmd('useCSS', true);
46351                                 editor.deferFocus();
46352                             }else{
46353                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46354                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46355                                 editor.deferFocus();
46356                             }
46357                         },
46358                         scope:editorcore,
46359                         clickEvent:'mousedown'
46360                     })
46361                 }
46362             );
46363         };
46364         // now add all the items...
46365         
46366
46367         if(!this.disable.alignments){
46368             tb.add(
46369                 '-',
46370                 btn('justifyleft'),
46371                 btn('justifycenter'),
46372                 btn('justifyright')
46373             );
46374         };
46375
46376         //if(!Roo.isSafari){
46377             if(!this.disable.links){
46378                 tb.add(
46379                     '-',
46380                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46381                 );
46382             };
46383
46384             if(!this.disable.lists){
46385                 tb.add(
46386                     '-',
46387                     btn('insertorderedlist'),
46388                     btn('insertunorderedlist')
46389                 );
46390             }
46391             if(!this.disable.sourceEdit){
46392                 tb.add(
46393                     '-',
46394                     btn('sourceedit', true, function(btn){
46395                         this.toggleSourceEdit(btn.pressed);
46396                     })
46397                 );
46398             }
46399         //}
46400         
46401         var smenu = { };
46402         // special menu.. - needs to be tidied up..
46403         if (!this.disable.special) {
46404             smenu = {
46405                 text: "&#169;",
46406                 cls: 'x-edit-none',
46407                 
46408                 menu : {
46409                     items : []
46410                 }
46411             };
46412             for (var i =0; i < this.specialChars.length; i++) {
46413                 smenu.menu.items.push({
46414                     
46415                     html: this.specialChars[i],
46416                     handler: function(a,b) {
46417                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46418                         //editor.insertAtCursor(a.html);
46419                         
46420                     },
46421                     tabIndex:-1
46422                 });
46423             }
46424             
46425             
46426             tb.add(smenu);
46427             
46428             
46429         }
46430         
46431         var cmenu = { };
46432         if (!this.disable.cleanStyles) {
46433             cmenu = {
46434                 cls: 'x-btn-icon x-btn-clear',
46435                 
46436                 menu : {
46437                     items : []
46438                 }
46439             };
46440             for (var i =0; i < this.cleanStyles.length; i++) {
46441                 cmenu.menu.items.push({
46442                     actiontype : this.cleanStyles[i],
46443                     html: 'Remove ' + this.cleanStyles[i],
46444                     handler: function(a,b) {
46445 //                        Roo.log(a);
46446 //                        Roo.log(b);
46447                         var c = Roo.get(editorcore.doc.body);
46448                         c.select('[style]').each(function(s) {
46449                             s.dom.style.removeProperty(a.actiontype);
46450                         });
46451                         editorcore.syncValue();
46452                     },
46453                     tabIndex:-1
46454                 });
46455             }
46456              cmenu.menu.items.push({
46457                 actiontype : 'tablewidths',
46458                 html: 'Remove Table Widths',
46459                 handler: function(a,b) {
46460                     editorcore.cleanTableWidths();
46461                     editorcore.syncValue();
46462                 },
46463                 tabIndex:-1
46464             });
46465             cmenu.menu.items.push({
46466                 actiontype : 'word',
46467                 html: 'Remove MS Word Formating',
46468                 handler: function(a,b) {
46469                     editorcore.cleanWord();
46470                     editorcore.syncValue();
46471                 },
46472                 tabIndex:-1
46473             });
46474             
46475             cmenu.menu.items.push({
46476                 actiontype : 'all',
46477                 html: 'Remove All Styles',
46478                 handler: function(a,b) {
46479                     
46480                     var c = Roo.get(editorcore.doc.body);
46481                     c.select('[style]').each(function(s) {
46482                         s.dom.removeAttribute('style');
46483                     });
46484                     editorcore.syncValue();
46485                 },
46486                 tabIndex:-1
46487             });
46488             
46489             cmenu.menu.items.push({
46490                 actiontype : 'all',
46491                 html: 'Remove All CSS Classes',
46492                 handler: function(a,b) {
46493                     
46494                     var c = Roo.get(editorcore.doc.body);
46495                     c.select('[class]').each(function(s) {
46496                         s.dom.removeAttribute('class');
46497                     });
46498                     editorcore.cleanWord();
46499                     editorcore.syncValue();
46500                 },
46501                 tabIndex:-1
46502             });
46503             
46504              cmenu.menu.items.push({
46505                 actiontype : 'tidy',
46506                 html: 'Tidy HTML Source',
46507                 handler: function(a,b) {
46508                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46509                     editorcore.syncValue();
46510                 },
46511                 tabIndex:-1
46512             });
46513             
46514             
46515             tb.add(cmenu);
46516         }
46517          
46518         if (!this.disable.specialElements) {
46519             var semenu = {
46520                 text: "Other;",
46521                 cls: 'x-edit-none',
46522                 menu : {
46523                     items : []
46524                 }
46525             };
46526             for (var i =0; i < this.specialElements.length; i++) {
46527                 semenu.menu.items.push(
46528                     Roo.apply({ 
46529                         handler: function(a,b) {
46530                             editor.insertAtCursor(this.ihtml);
46531                         }
46532                     }, this.specialElements[i])
46533                 );
46534                     
46535             }
46536             
46537             tb.add(semenu);
46538             
46539             
46540         }
46541          
46542         
46543         if (this.btns) {
46544             for(var i =0; i< this.btns.length;i++) {
46545                 var b = Roo.factory(this.btns[i],Roo.form);
46546                 b.cls =  'x-edit-none';
46547                 
46548                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46549                     b.cls += ' x-init-enable';
46550                 }
46551                 
46552                 b.scope = editorcore;
46553                 tb.add(b);
46554             }
46555         
46556         }
46557         
46558         
46559         
46560         // disable everything...
46561         
46562         this.tb.items.each(function(item){
46563             
46564            if(
46565                 item.id != editorcore.frameId+ '-sourceedit' && 
46566                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46567             ){
46568                 
46569                 item.disable();
46570             }
46571         });
46572         this.rendered = true;
46573         
46574         // the all the btns;
46575         editor.on('editorevent', this.updateToolbar, this);
46576         // other toolbars need to implement this..
46577         //editor.on('editmodechange', this.updateToolbar, this);
46578     },
46579     
46580     
46581     relayBtnCmd : function(btn) {
46582         this.editorcore.relayCmd(btn.cmd);
46583     },
46584     // private used internally
46585     createLink : function(){
46586         Roo.log("create link?");
46587         var url = prompt(this.createLinkText, this.defaultLinkValue);
46588         if(url && url != 'http:/'+'/'){
46589             this.editorcore.relayCmd('createlink', url);
46590         }
46591     },
46592
46593     
46594     /**
46595      * Protected method that will not generally be called directly. It triggers
46596      * a toolbar update by reading the markup state of the current selection in the editor.
46597      */
46598     updateToolbar: function(){
46599
46600         if(!this.editorcore.activated){
46601             this.editor.onFirstFocus();
46602             return;
46603         }
46604
46605         var btns = this.tb.items.map, 
46606             doc = this.editorcore.doc,
46607             frameId = this.editorcore.frameId;
46608
46609         if(!this.disable.font && !Roo.isSafari){
46610             /*
46611             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46612             if(name != this.fontSelect.dom.value){
46613                 this.fontSelect.dom.value = name;
46614             }
46615             */
46616         }
46617         if(!this.disable.format){
46618             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46619             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46620             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46621             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46622         }
46623         if(!this.disable.alignments){
46624             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46625             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46626             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46627         }
46628         if(!Roo.isSafari && !this.disable.lists){
46629             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46630             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46631         }
46632         
46633         var ans = this.editorcore.getAllAncestors();
46634         if (this.formatCombo) {
46635             
46636             
46637             var store = this.formatCombo.store;
46638             this.formatCombo.setValue("");
46639             for (var i =0; i < ans.length;i++) {
46640                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46641                     // select it..
46642                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46643                     break;
46644                 }
46645             }
46646         }
46647         
46648         
46649         
46650         // hides menus... - so this cant be on a menu...
46651         Roo.menu.MenuMgr.hideAll();
46652
46653         //this.editorsyncValue();
46654     },
46655    
46656     
46657     createFontOptions : function(){
46658         var buf = [], fs = this.fontFamilies, ff, lc;
46659         
46660         
46661         
46662         for(var i = 0, len = fs.length; i< len; i++){
46663             ff = fs[i];
46664             lc = ff.toLowerCase();
46665             buf.push(
46666                 '<option value="',lc,'" style="font-family:',ff,';"',
46667                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46668                     ff,
46669                 '</option>'
46670             );
46671         }
46672         return buf.join('');
46673     },
46674     
46675     toggleSourceEdit : function(sourceEditMode){
46676         
46677         Roo.log("toolbar toogle");
46678         if(sourceEditMode === undefined){
46679             sourceEditMode = !this.sourceEditMode;
46680         }
46681         this.sourceEditMode = sourceEditMode === true;
46682         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46683         // just toggle the button?
46684         if(btn.pressed !== this.sourceEditMode){
46685             btn.toggle(this.sourceEditMode);
46686             return;
46687         }
46688         
46689         if(sourceEditMode){
46690             Roo.log("disabling buttons");
46691             this.tb.items.each(function(item){
46692                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46693                     item.disable();
46694                 }
46695             });
46696           
46697         }else{
46698             Roo.log("enabling buttons");
46699             if(this.editorcore.initialized){
46700                 this.tb.items.each(function(item){
46701                     item.enable();
46702                 });
46703             }
46704             
46705         }
46706         Roo.log("calling toggole on editor");
46707         // tell the editor that it's been pressed..
46708         this.editor.toggleSourceEdit(sourceEditMode);
46709        
46710     },
46711      /**
46712      * Object collection of toolbar tooltips for the buttons in the editor. The key
46713      * is the command id associated with that button and the value is a valid QuickTips object.
46714      * For example:
46715 <pre><code>
46716 {
46717     bold : {
46718         title: 'Bold (Ctrl+B)',
46719         text: 'Make the selected text bold.',
46720         cls: 'x-html-editor-tip'
46721     },
46722     italic : {
46723         title: 'Italic (Ctrl+I)',
46724         text: 'Make the selected text italic.',
46725         cls: 'x-html-editor-tip'
46726     },
46727     ...
46728 </code></pre>
46729     * @type Object
46730      */
46731     buttonTips : {
46732         bold : {
46733             title: 'Bold (Ctrl+B)',
46734             text: 'Make the selected text bold.',
46735             cls: 'x-html-editor-tip'
46736         },
46737         italic : {
46738             title: 'Italic (Ctrl+I)',
46739             text: 'Make the selected text italic.',
46740             cls: 'x-html-editor-tip'
46741         },
46742         underline : {
46743             title: 'Underline (Ctrl+U)',
46744             text: 'Underline the selected text.',
46745             cls: 'x-html-editor-tip'
46746         },
46747         strikethrough : {
46748             title: 'Strikethrough',
46749             text: 'Strikethrough the selected text.',
46750             cls: 'x-html-editor-tip'
46751         },
46752         increasefontsize : {
46753             title: 'Grow Text',
46754             text: 'Increase the font size.',
46755             cls: 'x-html-editor-tip'
46756         },
46757         decreasefontsize : {
46758             title: 'Shrink Text',
46759             text: 'Decrease the font size.',
46760             cls: 'x-html-editor-tip'
46761         },
46762         backcolor : {
46763             title: 'Text Highlight Color',
46764             text: 'Change the background color of the selected text.',
46765             cls: 'x-html-editor-tip'
46766         },
46767         forecolor : {
46768             title: 'Font Color',
46769             text: 'Change the color of the selected text.',
46770             cls: 'x-html-editor-tip'
46771         },
46772         justifyleft : {
46773             title: 'Align Text Left',
46774             text: 'Align text to the left.',
46775             cls: 'x-html-editor-tip'
46776         },
46777         justifycenter : {
46778             title: 'Center Text',
46779             text: 'Center text in the editor.',
46780             cls: 'x-html-editor-tip'
46781         },
46782         justifyright : {
46783             title: 'Align Text Right',
46784             text: 'Align text to the right.',
46785             cls: 'x-html-editor-tip'
46786         },
46787         insertunorderedlist : {
46788             title: 'Bullet List',
46789             text: 'Start a bulleted list.',
46790             cls: 'x-html-editor-tip'
46791         },
46792         insertorderedlist : {
46793             title: 'Numbered List',
46794             text: 'Start a numbered list.',
46795             cls: 'x-html-editor-tip'
46796         },
46797         createlink : {
46798             title: 'Hyperlink',
46799             text: 'Make the selected text a hyperlink.',
46800             cls: 'x-html-editor-tip'
46801         },
46802         sourceedit : {
46803             title: 'Source Edit',
46804             text: 'Switch to source editing mode.',
46805             cls: 'x-html-editor-tip'
46806         }
46807     },
46808     // private
46809     onDestroy : function(){
46810         if(this.rendered){
46811             
46812             this.tb.items.each(function(item){
46813                 if(item.menu){
46814                     item.menu.removeAll();
46815                     if(item.menu.el){
46816                         item.menu.el.destroy();
46817                     }
46818                 }
46819                 item.destroy();
46820             });
46821              
46822         }
46823     },
46824     onFirstFocus: function() {
46825         this.tb.items.each(function(item){
46826            item.enable();
46827         });
46828     }
46829 });
46830
46831
46832
46833
46834 // <script type="text/javascript">
46835 /*
46836  * Based on
46837  * Ext JS Library 1.1.1
46838  * Copyright(c) 2006-2007, Ext JS, LLC.
46839  *  
46840  
46841  */
46842
46843  
46844 /**
46845  * @class Roo.form.HtmlEditor.ToolbarContext
46846  * Context Toolbar
46847  * 
46848  * Usage:
46849  *
46850  new Roo.form.HtmlEditor({
46851     ....
46852     toolbars : [
46853         { xtype: 'ToolbarStandard', styles : {} }
46854         { xtype: 'ToolbarContext', disable : {} }
46855     ]
46856 })
46857
46858      
46859  * 
46860  * @config : {Object} disable List of elements to disable.. (not done yet.)
46861  * @config : {Object} styles  Map of styles available.
46862  * 
46863  */
46864
46865 Roo.form.HtmlEditor.ToolbarContext = function(config)
46866 {
46867     
46868     Roo.apply(this, config);
46869     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46870     // dont call parent... till later.
46871     this.styles = this.styles || {};
46872 }
46873
46874  
46875
46876 Roo.form.HtmlEditor.ToolbarContext.types = {
46877     'IMG' : {
46878         width : {
46879             title: "Width",
46880             width: 40
46881         },
46882         height:  {
46883             title: "Height",
46884             width: 40
46885         },
46886         align: {
46887             title: "Align",
46888             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46889             width : 80
46890             
46891         },
46892         border: {
46893             title: "Border",
46894             width: 40
46895         },
46896         alt: {
46897             title: "Alt",
46898             width: 120
46899         },
46900         src : {
46901             title: "Src",
46902             width: 220
46903         }
46904         
46905     },
46906     'A' : {
46907         name : {
46908             title: "Name",
46909             width: 50
46910         },
46911         target:  {
46912             title: "Target",
46913             width: 120
46914         },
46915         href:  {
46916             title: "Href",
46917             width: 220
46918         } // border?
46919         
46920     },
46921     'TABLE' : {
46922         rows : {
46923             title: "Rows",
46924             width: 20
46925         },
46926         cols : {
46927             title: "Cols",
46928             width: 20
46929         },
46930         width : {
46931             title: "Width",
46932             width: 40
46933         },
46934         height : {
46935             title: "Height",
46936             width: 40
46937         },
46938         border : {
46939             title: "Border",
46940             width: 20
46941         }
46942     },
46943     'TD' : {
46944         width : {
46945             title: "Width",
46946             width: 40
46947         },
46948         height : {
46949             title: "Height",
46950             width: 40
46951         },   
46952         align: {
46953             title: "Align",
46954             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46955             width: 80
46956         },
46957         valign: {
46958             title: "Valign",
46959             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46960             width: 80
46961         },
46962         colspan: {
46963             title: "Colspan",
46964             width: 20
46965             
46966         },
46967          'font-family'  : {
46968             title : "Font",
46969             style : 'fontFamily',
46970             displayField: 'display',
46971             optname : 'font-family',
46972             width: 140
46973         }
46974     },
46975     'INPUT' : {
46976         name : {
46977             title: "name",
46978             width: 120
46979         },
46980         value : {
46981             title: "Value",
46982             width: 120
46983         },
46984         width : {
46985             title: "Width",
46986             width: 40
46987         }
46988     },
46989     'LABEL' : {
46990         'for' : {
46991             title: "For",
46992             width: 120
46993         }
46994     },
46995     'TEXTAREA' : {
46996           name : {
46997             title: "name",
46998             width: 120
46999         },
47000         rows : {
47001             title: "Rows",
47002             width: 20
47003         },
47004         cols : {
47005             title: "Cols",
47006             width: 20
47007         }
47008     },
47009     'SELECT' : {
47010         name : {
47011             title: "name",
47012             width: 120
47013         },
47014         selectoptions : {
47015             title: "Options",
47016             width: 200
47017         }
47018     },
47019     
47020     // should we really allow this??
47021     // should this just be 
47022     'BODY' : {
47023         title : {
47024             title: "Title",
47025             width: 200,
47026             disabled : true
47027         }
47028     },
47029     'SPAN' : {
47030         'font-family'  : {
47031             title : "Font",
47032             style : 'fontFamily',
47033             displayField: 'display',
47034             optname : 'font-family',
47035             width: 140
47036         }
47037     },
47038     'DIV' : {
47039         'font-family'  : {
47040             title : "Font",
47041             style : 'fontFamily',
47042             displayField: 'display',
47043             optname : 'font-family',
47044             width: 140
47045         }
47046     },
47047      'P' : {
47048         'font-family'  : {
47049             title : "Font",
47050             style : 'fontFamily',
47051             displayField: 'display',
47052             optname : 'font-family',
47053             width: 140
47054         }
47055     },
47056     
47057     '*' : {
47058         // empty..
47059     }
47060
47061 };
47062
47063 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47064 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47065
47066 Roo.form.HtmlEditor.ToolbarContext.options = {
47067         'font-family'  : [ 
47068                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47069                 [ 'Courier New', 'Courier New'],
47070                 [ 'Tahoma', 'Tahoma'],
47071                 [ 'Times New Roman,serif', 'Times'],
47072                 [ 'Verdana','Verdana' ]
47073         ]
47074 };
47075
47076 // fixme - these need to be configurable..
47077  
47078
47079 //Roo.form.HtmlEditor.ToolbarContext.types
47080
47081
47082 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47083     
47084     tb: false,
47085     
47086     rendered: false,
47087     
47088     editor : false,
47089     editorcore : false,
47090     /**
47091      * @cfg {Object} disable  List of toolbar elements to disable
47092          
47093      */
47094     disable : false,
47095     /**
47096      * @cfg {Object} styles List of styles 
47097      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47098      *
47099      * These must be defined in the page, so they get rendered correctly..
47100      * .headline { }
47101      * TD.underline { }
47102      * 
47103      */
47104     styles : false,
47105     
47106     options: false,
47107     
47108     toolbars : false,
47109     
47110     init : function(editor)
47111     {
47112         this.editor = editor;
47113         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47114         var editorcore = this.editorcore;
47115         
47116         var fid = editorcore.frameId;
47117         var etb = this;
47118         function btn(id, toggle, handler){
47119             var xid = fid + '-'+ id ;
47120             return {
47121                 id : xid,
47122                 cmd : id,
47123                 cls : 'x-btn-icon x-edit-'+id,
47124                 enableToggle:toggle !== false,
47125                 scope: editorcore, // was editor...
47126                 handler:handler||editorcore.relayBtnCmd,
47127                 clickEvent:'mousedown',
47128                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47129                 tabIndex:-1
47130             };
47131         }
47132         // create a new element.
47133         var wdiv = editor.wrap.createChild({
47134                 tag: 'div'
47135             }, editor.wrap.dom.firstChild.nextSibling, true);
47136         
47137         // can we do this more than once??
47138         
47139          // stop form submits
47140       
47141  
47142         // disable everything...
47143         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47144         this.toolbars = {};
47145            
47146         for (var i in  ty) {
47147           
47148             this.toolbars[i] = this.buildToolbar(ty[i],i);
47149         }
47150         this.tb = this.toolbars.BODY;
47151         this.tb.el.show();
47152         this.buildFooter();
47153         this.footer.show();
47154         editor.on('hide', function( ) { this.footer.hide() }, this);
47155         editor.on('show', function( ) { this.footer.show() }, this);
47156         
47157          
47158         this.rendered = true;
47159         
47160         // the all the btns;
47161         editor.on('editorevent', this.updateToolbar, this);
47162         // other toolbars need to implement this..
47163         //editor.on('editmodechange', this.updateToolbar, this);
47164     },
47165     
47166     
47167     
47168     /**
47169      * Protected method that will not generally be called directly. It triggers
47170      * a toolbar update by reading the markup state of the current selection in the editor.
47171      *
47172      * Note you can force an update by calling on('editorevent', scope, false)
47173      */
47174     updateToolbar: function(editor,ev,sel){
47175
47176         //Roo.log(ev);
47177         // capture mouse up - this is handy for selecting images..
47178         // perhaps should go somewhere else...
47179         if(!this.editorcore.activated){
47180              this.editor.onFirstFocus();
47181             return;
47182         }
47183         
47184         
47185         
47186         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47187         // selectNode - might want to handle IE?
47188         if (ev &&
47189             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47190             ev.target && ev.target.tagName == 'IMG') {
47191             // they have click on an image...
47192             // let's see if we can change the selection...
47193             sel = ev.target;
47194          
47195               var nodeRange = sel.ownerDocument.createRange();
47196             try {
47197                 nodeRange.selectNode(sel);
47198             } catch (e) {
47199                 nodeRange.selectNodeContents(sel);
47200             }
47201             //nodeRange.collapse(true);
47202             var s = this.editorcore.win.getSelection();
47203             s.removeAllRanges();
47204             s.addRange(nodeRange);
47205         }  
47206         
47207       
47208         var updateFooter = sel ? false : true;
47209         
47210         
47211         var ans = this.editorcore.getAllAncestors();
47212         
47213         // pick
47214         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47215         
47216         if (!sel) { 
47217             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47218             sel = sel ? sel : this.editorcore.doc.body;
47219             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47220             
47221         }
47222         // pick a menu that exists..
47223         var tn = sel.tagName.toUpperCase();
47224         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47225         
47226         tn = sel.tagName.toUpperCase();
47227         
47228         var lastSel = this.tb.selectedNode;
47229         
47230         this.tb.selectedNode = sel;
47231         
47232         // if current menu does not match..
47233         
47234         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47235                 
47236             this.tb.el.hide();
47237             ///console.log("show: " + tn);
47238             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47239             this.tb.el.show();
47240             // update name
47241             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47242             
47243             
47244             // update attributes
47245             if (this.tb.fields) {
47246                 this.tb.fields.each(function(e) {
47247                     if (e.stylename) {
47248                         e.setValue(sel.style[e.stylename]);
47249                         return;
47250                     } 
47251                    e.setValue(sel.getAttribute(e.attrname));
47252                 });
47253             }
47254             
47255             var hasStyles = false;
47256             for(var i in this.styles) {
47257                 hasStyles = true;
47258                 break;
47259             }
47260             
47261             // update styles
47262             if (hasStyles) { 
47263                 var st = this.tb.fields.item(0);
47264                 
47265                 st.store.removeAll();
47266                
47267                 
47268                 var cn = sel.className.split(/\s+/);
47269                 
47270                 var avs = [];
47271                 if (this.styles['*']) {
47272                     
47273                     Roo.each(this.styles['*'], function(v) {
47274                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47275                     });
47276                 }
47277                 if (this.styles[tn]) { 
47278                     Roo.each(this.styles[tn], function(v) {
47279                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47280                     });
47281                 }
47282                 
47283                 st.store.loadData(avs);
47284                 st.collapse();
47285                 st.setValue(cn);
47286             }
47287             // flag our selected Node.
47288             this.tb.selectedNode = sel;
47289            
47290            
47291             Roo.menu.MenuMgr.hideAll();
47292
47293         }
47294         
47295         if (!updateFooter) {
47296             //this.footDisp.dom.innerHTML = ''; 
47297             return;
47298         }
47299         // update the footer
47300         //
47301         var html = '';
47302         
47303         this.footerEls = ans.reverse();
47304         Roo.each(this.footerEls, function(a,i) {
47305             if (!a) { return; }
47306             html += html.length ? ' &gt; '  :  '';
47307             
47308             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47309             
47310         });
47311        
47312         // 
47313         var sz = this.footDisp.up('td').getSize();
47314         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47315         this.footDisp.dom.style.marginLeft = '5px';
47316         
47317         this.footDisp.dom.style.overflow = 'hidden';
47318         
47319         this.footDisp.dom.innerHTML = html;
47320             
47321         //this.editorsyncValue();
47322     },
47323      
47324     
47325    
47326        
47327     // private
47328     onDestroy : function(){
47329         if(this.rendered){
47330             
47331             this.tb.items.each(function(item){
47332                 if(item.menu){
47333                     item.menu.removeAll();
47334                     if(item.menu.el){
47335                         item.menu.el.destroy();
47336                     }
47337                 }
47338                 item.destroy();
47339             });
47340              
47341         }
47342     },
47343     onFirstFocus: function() {
47344         // need to do this for all the toolbars..
47345         this.tb.items.each(function(item){
47346            item.enable();
47347         });
47348     },
47349     buildToolbar: function(tlist, nm)
47350     {
47351         var editor = this.editor;
47352         var editorcore = this.editorcore;
47353          // create a new element.
47354         var wdiv = editor.wrap.createChild({
47355                 tag: 'div'
47356             }, editor.wrap.dom.firstChild.nextSibling, true);
47357         
47358        
47359         var tb = new Roo.Toolbar(wdiv);
47360         // add the name..
47361         
47362         tb.add(nm+ ":&nbsp;");
47363         
47364         var styles = [];
47365         for(var i in this.styles) {
47366             styles.push(i);
47367         }
47368         
47369         // styles...
47370         if (styles && styles.length) {
47371             
47372             // this needs a multi-select checkbox...
47373             tb.addField( new Roo.form.ComboBox({
47374                 store: new Roo.data.SimpleStore({
47375                     id : 'val',
47376                     fields: ['val', 'selected'],
47377                     data : [] 
47378                 }),
47379                 name : '-roo-edit-className',
47380                 attrname : 'className',
47381                 displayField: 'val',
47382                 typeAhead: false,
47383                 mode: 'local',
47384                 editable : false,
47385                 triggerAction: 'all',
47386                 emptyText:'Select Style',
47387                 selectOnFocus:true,
47388                 width: 130,
47389                 listeners : {
47390                     'select': function(c, r, i) {
47391                         // initial support only for on class per el..
47392                         tb.selectedNode.className =  r ? r.get('val') : '';
47393                         editorcore.syncValue();
47394                     }
47395                 }
47396     
47397             }));
47398         }
47399         
47400         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47401         var tbops = tbc.options;
47402         
47403         for (var i in tlist) {
47404             
47405             var item = tlist[i];
47406             tb.add(item.title + ":&nbsp;");
47407             
47408             
47409             //optname == used so you can configure the options available..
47410             var opts = item.opts ? item.opts : false;
47411             if (item.optname) {
47412                 opts = tbops[item.optname];
47413            
47414             }
47415             
47416             if (opts) {
47417                 // opts == pulldown..
47418                 tb.addField( new Roo.form.ComboBox({
47419                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47420                         id : 'val',
47421                         fields: ['val', 'display'],
47422                         data : opts  
47423                     }),
47424                     name : '-roo-edit-' + i,
47425                     attrname : i,
47426                     stylename : item.style ? item.style : false,
47427                     displayField: item.displayField ? item.displayField : 'val',
47428                     valueField :  'val',
47429                     typeAhead: false,
47430                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47431                     editable : false,
47432                     triggerAction: 'all',
47433                     emptyText:'Select',
47434                     selectOnFocus:true,
47435                     width: item.width ? item.width  : 130,
47436                     listeners : {
47437                         'select': function(c, r, i) {
47438                             if (c.stylename) {
47439                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47440                                 return;
47441                             }
47442                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47443                         }
47444                     }
47445
47446                 }));
47447                 continue;
47448                     
47449                  
47450                 
47451                 tb.addField( new Roo.form.TextField({
47452                     name: i,
47453                     width: 100,
47454                     //allowBlank:false,
47455                     value: ''
47456                 }));
47457                 continue;
47458             }
47459             tb.addField( new Roo.form.TextField({
47460                 name: '-roo-edit-' + i,
47461                 attrname : i,
47462                 
47463                 width: item.width,
47464                 //allowBlank:true,
47465                 value: '',
47466                 listeners: {
47467                     'change' : function(f, nv, ov) {
47468                         tb.selectedNode.setAttribute(f.attrname, nv);
47469                         editorcore.syncValue();
47470                     }
47471                 }
47472             }));
47473              
47474         }
47475         
47476         var _this = this;
47477         
47478         if(nm == 'BODY'){
47479             tb.addSeparator();
47480         
47481             tb.addButton( {
47482                 text: 'Stylesheets',
47483
47484                 listeners : {
47485                     click : function ()
47486                     {
47487                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47488                     }
47489                 }
47490             });
47491         }
47492         
47493         tb.addFill();
47494         tb.addButton( {
47495             text: 'Remove Tag',
47496     
47497             listeners : {
47498                 click : function ()
47499                 {
47500                     // remove
47501                     // undo does not work.
47502                      
47503                     var sn = tb.selectedNode;
47504                     
47505                     var pn = sn.parentNode;
47506                     
47507                     var stn =  sn.childNodes[0];
47508                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47509                     while (sn.childNodes.length) {
47510                         var node = sn.childNodes[0];
47511                         sn.removeChild(node);
47512                         //Roo.log(node);
47513                         pn.insertBefore(node, sn);
47514                         
47515                     }
47516                     pn.removeChild(sn);
47517                     var range = editorcore.createRange();
47518         
47519                     range.setStart(stn,0);
47520                     range.setEnd(en,0); //????
47521                     //range.selectNode(sel);
47522                     
47523                     
47524                     var selection = editorcore.getSelection();
47525                     selection.removeAllRanges();
47526                     selection.addRange(range);
47527                     
47528                     
47529                     
47530                     //_this.updateToolbar(null, null, pn);
47531                     _this.updateToolbar(null, null, null);
47532                     _this.footDisp.dom.innerHTML = ''; 
47533                 }
47534             }
47535             
47536                     
47537                 
47538             
47539         });
47540         
47541         
47542         tb.el.on('click', function(e){
47543             e.preventDefault(); // what does this do?
47544         });
47545         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47546         tb.el.hide();
47547         tb.name = nm;
47548         // dont need to disable them... as they will get hidden
47549         return tb;
47550          
47551         
47552     },
47553     buildFooter : function()
47554     {
47555         
47556         var fel = this.editor.wrap.createChild();
47557         this.footer = new Roo.Toolbar(fel);
47558         // toolbar has scrolly on left / right?
47559         var footDisp= new Roo.Toolbar.Fill();
47560         var _t = this;
47561         this.footer.add(
47562             {
47563                 text : '&lt;',
47564                 xtype: 'Button',
47565                 handler : function() {
47566                     _t.footDisp.scrollTo('left',0,true)
47567                 }
47568             }
47569         );
47570         this.footer.add( footDisp );
47571         this.footer.add( 
47572             {
47573                 text : '&gt;',
47574                 xtype: 'Button',
47575                 handler : function() {
47576                     // no animation..
47577                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47578                 }
47579             }
47580         );
47581         var fel = Roo.get(footDisp.el);
47582         fel.addClass('x-editor-context');
47583         this.footDispWrap = fel; 
47584         this.footDispWrap.overflow  = 'hidden';
47585         
47586         this.footDisp = fel.createChild();
47587         this.footDispWrap.on('click', this.onContextClick, this)
47588         
47589         
47590     },
47591     onContextClick : function (ev,dom)
47592     {
47593         ev.preventDefault();
47594         var  cn = dom.className;
47595         //Roo.log(cn);
47596         if (!cn.match(/x-ed-loc-/)) {
47597             return;
47598         }
47599         var n = cn.split('-').pop();
47600         var ans = this.footerEls;
47601         var sel = ans[n];
47602         
47603          // pick
47604         var range = this.editorcore.createRange();
47605         
47606         range.selectNodeContents(sel);
47607         //range.selectNode(sel);
47608         
47609         
47610         var selection = this.editorcore.getSelection();
47611         selection.removeAllRanges();
47612         selection.addRange(range);
47613         
47614         
47615         
47616         this.updateToolbar(null, null, sel);
47617         
47618         
47619     }
47620     
47621     
47622     
47623     
47624     
47625 });
47626
47627
47628
47629
47630
47631 /*
47632  * Based on:
47633  * Ext JS Library 1.1.1
47634  * Copyright(c) 2006-2007, Ext JS, LLC.
47635  *
47636  * Originally Released Under LGPL - original licence link has changed is not relivant.
47637  *
47638  * Fork - LGPL
47639  * <script type="text/javascript">
47640  */
47641  
47642 /**
47643  * @class Roo.form.BasicForm
47644  * @extends Roo.util.Observable
47645  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47646  * @constructor
47647  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47648  * @param {Object} config Configuration options
47649  */
47650 Roo.form.BasicForm = function(el, config){
47651     this.allItems = [];
47652     this.childForms = [];
47653     Roo.apply(this, config);
47654     /*
47655      * The Roo.form.Field items in this form.
47656      * @type MixedCollection
47657      */
47658      
47659      
47660     this.items = new Roo.util.MixedCollection(false, function(o){
47661         return o.id || (o.id = Roo.id());
47662     });
47663     this.addEvents({
47664         /**
47665          * @event beforeaction
47666          * Fires before any action is performed. Return false to cancel the action.
47667          * @param {Form} this
47668          * @param {Action} action The action to be performed
47669          */
47670         beforeaction: true,
47671         /**
47672          * @event actionfailed
47673          * Fires when an action fails.
47674          * @param {Form} this
47675          * @param {Action} action The action that failed
47676          */
47677         actionfailed : true,
47678         /**
47679          * @event actioncomplete
47680          * Fires when an action is completed.
47681          * @param {Form} this
47682          * @param {Action} action The action that completed
47683          */
47684         actioncomplete : true
47685     });
47686     if(el){
47687         this.initEl(el);
47688     }
47689     Roo.form.BasicForm.superclass.constructor.call(this);
47690     
47691     Roo.form.BasicForm.popover.apply();
47692 };
47693
47694 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47695     /**
47696      * @cfg {String} method
47697      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47698      */
47699     /**
47700      * @cfg {DataReader} reader
47701      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47702      * This is optional as there is built-in support for processing JSON.
47703      */
47704     /**
47705      * @cfg {DataReader} errorReader
47706      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47707      * This is completely optional as there is built-in support for processing JSON.
47708      */
47709     /**
47710      * @cfg {String} url
47711      * The URL to use for form actions if one isn't supplied in the action options.
47712      */
47713     /**
47714      * @cfg {Boolean} fileUpload
47715      * Set to true if this form is a file upload.
47716      */
47717      
47718     /**
47719      * @cfg {Object} baseParams
47720      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47721      */
47722      /**
47723      
47724     /**
47725      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47726      */
47727     timeout: 30,
47728
47729     // private
47730     activeAction : null,
47731
47732     /**
47733      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47734      * or setValues() data instead of when the form was first created.
47735      */
47736     trackResetOnLoad : false,
47737     
47738     
47739     /**
47740      * childForms - used for multi-tab forms
47741      * @type {Array}
47742      */
47743     childForms : false,
47744     
47745     /**
47746      * allItems - full list of fields.
47747      * @type {Array}
47748      */
47749     allItems : false,
47750     
47751     /**
47752      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47753      * element by passing it or its id or mask the form itself by passing in true.
47754      * @type Mixed
47755      */
47756     waitMsgTarget : false,
47757     
47758     /**
47759      * @type Boolean
47760      */
47761     disableMask : false,
47762     
47763     /**
47764      * @cfg {Boolean} errorMask (true|false) default false
47765      */
47766     errorMask : false,
47767     
47768     /**
47769      * @cfg {Number} maskOffset Default 100
47770      */
47771     maskOffset : 100,
47772
47773     // private
47774     initEl : function(el){
47775         this.el = Roo.get(el);
47776         this.id = this.el.id || Roo.id();
47777         this.el.on('submit', this.onSubmit, this);
47778         this.el.addClass('x-form');
47779     },
47780
47781     // private
47782     onSubmit : function(e){
47783         e.stopEvent();
47784     },
47785
47786     /**
47787      * Returns true if client-side validation on the form is successful.
47788      * @return Boolean
47789      */
47790     isValid : function(){
47791         var valid = true;
47792         var target = false;
47793         this.items.each(function(f){
47794             if(f.validate()){
47795                 return;
47796             }
47797             
47798             valid = false;
47799                 
47800             if(!target && f.el.isVisible(true)){
47801                 target = f;
47802             }
47803         });
47804         
47805         if(this.errorMask && !valid){
47806             Roo.form.BasicForm.popover.mask(this, target);
47807         }
47808         
47809         return valid;
47810     },
47811     /**
47812      * Returns array of invalid form fields.
47813      * @return Array
47814      */
47815     
47816     invalidFields : function()
47817     {
47818         var ret = [];
47819         this.items.each(function(f){
47820             if(f.validate()){
47821                 return;
47822             }
47823             ret.push(f);
47824             
47825         });
47826         
47827         return ret;
47828     },
47829     
47830     
47831     /**
47832      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47833      * @return Boolean
47834      */
47835     isDirty : function(){
47836         var dirty = false;
47837         this.items.each(function(f){
47838            if(f.isDirty()){
47839                dirty = true;
47840                return false;
47841            }
47842         });
47843         return dirty;
47844     },
47845     
47846     /**
47847      * Returns true if any fields in this form have changed since their original load. (New version)
47848      * @return Boolean
47849      */
47850     
47851     hasChanged : function()
47852     {
47853         var dirty = false;
47854         this.items.each(function(f){
47855            if(f.hasChanged()){
47856                dirty = true;
47857                return false;
47858            }
47859         });
47860         return dirty;
47861         
47862     },
47863     /**
47864      * Resets all hasChanged to 'false' -
47865      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47866      * So hasChanged storage is only to be used for this purpose
47867      * @return Boolean
47868      */
47869     resetHasChanged : function()
47870     {
47871         this.items.each(function(f){
47872            f.resetHasChanged();
47873         });
47874         
47875     },
47876     
47877     
47878     /**
47879      * Performs a predefined action (submit or load) or custom actions you define on this form.
47880      * @param {String} actionName The name of the action type
47881      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47882      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47883      * accept other config options):
47884      * <pre>
47885 Property          Type             Description
47886 ----------------  ---------------  ----------------------------------------------------------------------------------
47887 url               String           The url for the action (defaults to the form's url)
47888 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47889 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47890 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47891                                    validate the form on the client (defaults to false)
47892      * </pre>
47893      * @return {BasicForm} this
47894      */
47895     doAction : function(action, options){
47896         if(typeof action == 'string'){
47897             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47898         }
47899         if(this.fireEvent('beforeaction', this, action) !== false){
47900             this.beforeAction(action);
47901             action.run.defer(100, action);
47902         }
47903         return this;
47904     },
47905
47906     /**
47907      * Shortcut to do a submit action.
47908      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47909      * @return {BasicForm} this
47910      */
47911     submit : function(options){
47912         this.doAction('submit', options);
47913         return this;
47914     },
47915
47916     /**
47917      * Shortcut to do a load action.
47918      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47919      * @return {BasicForm} this
47920      */
47921     load : function(options){
47922         this.doAction('load', options);
47923         return this;
47924     },
47925
47926     /**
47927      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47928      * @param {Record} record The record to edit
47929      * @return {BasicForm} this
47930      */
47931     updateRecord : function(record){
47932         record.beginEdit();
47933         var fs = record.fields;
47934         fs.each(function(f){
47935             var field = this.findField(f.name);
47936             if(field){
47937                 record.set(f.name, field.getValue());
47938             }
47939         }, this);
47940         record.endEdit();
47941         return this;
47942     },
47943
47944     /**
47945      * Loads an Roo.data.Record into this form.
47946      * @param {Record} record The record to load
47947      * @return {BasicForm} this
47948      */
47949     loadRecord : function(record){
47950         this.setValues(record.data);
47951         return this;
47952     },
47953
47954     // private
47955     beforeAction : function(action){
47956         var o = action.options;
47957         
47958         if(!this.disableMask) {
47959             if(this.waitMsgTarget === true){
47960                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47961             }else if(this.waitMsgTarget){
47962                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47963                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47964             }else {
47965                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47966             }
47967         }
47968         
47969          
47970     },
47971
47972     // private
47973     afterAction : function(action, success){
47974         this.activeAction = null;
47975         var o = action.options;
47976         
47977         if(!this.disableMask) {
47978             if(this.waitMsgTarget === true){
47979                 this.el.unmask();
47980             }else if(this.waitMsgTarget){
47981                 this.waitMsgTarget.unmask();
47982             }else{
47983                 Roo.MessageBox.updateProgress(1);
47984                 Roo.MessageBox.hide();
47985             }
47986         }
47987         
47988         if(success){
47989             if(o.reset){
47990                 this.reset();
47991             }
47992             Roo.callback(o.success, o.scope, [this, action]);
47993             this.fireEvent('actioncomplete', this, action);
47994             
47995         }else{
47996             
47997             // failure condition..
47998             // we have a scenario where updates need confirming.
47999             // eg. if a locking scenario exists..
48000             // we look for { errors : { needs_confirm : true }} in the response.
48001             if (
48002                 (typeof(action.result) != 'undefined')  &&
48003                 (typeof(action.result.errors) != 'undefined')  &&
48004                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48005            ){
48006                 var _t = this;
48007                 Roo.MessageBox.confirm(
48008                     "Change requires confirmation",
48009                     action.result.errorMsg,
48010                     function(r) {
48011                         if (r != 'yes') {
48012                             return;
48013                         }
48014                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48015                     }
48016                     
48017                 );
48018                 
48019                 
48020                 
48021                 return;
48022             }
48023             
48024             Roo.callback(o.failure, o.scope, [this, action]);
48025             // show an error message if no failed handler is set..
48026             if (!this.hasListener('actionfailed')) {
48027                 Roo.MessageBox.alert("Error",
48028                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48029                         action.result.errorMsg :
48030                         "Saving Failed, please check your entries or try again"
48031                 );
48032             }
48033             
48034             this.fireEvent('actionfailed', this, action);
48035         }
48036         
48037     },
48038
48039     /**
48040      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48041      * @param {String} id The value to search for
48042      * @return Field
48043      */
48044     findField : function(id){
48045         var field = this.items.get(id);
48046         if(!field){
48047             this.items.each(function(f){
48048                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48049                     field = f;
48050                     return false;
48051                 }
48052             });
48053         }
48054         return field || null;
48055     },
48056
48057     /**
48058      * Add a secondary form to this one, 
48059      * Used to provide tabbed forms. One form is primary, with hidden values 
48060      * which mirror the elements from the other forms.
48061      * 
48062      * @param {Roo.form.Form} form to add.
48063      * 
48064      */
48065     addForm : function(form)
48066     {
48067        
48068         if (this.childForms.indexOf(form) > -1) {
48069             // already added..
48070             return;
48071         }
48072         this.childForms.push(form);
48073         var n = '';
48074         Roo.each(form.allItems, function (fe) {
48075             
48076             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48077             if (this.findField(n)) { // already added..
48078                 return;
48079             }
48080             var add = new Roo.form.Hidden({
48081                 name : n
48082             });
48083             add.render(this.el);
48084             
48085             this.add( add );
48086         }, this);
48087         
48088     },
48089     /**
48090      * Mark fields in this form invalid in bulk.
48091      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48092      * @return {BasicForm} this
48093      */
48094     markInvalid : function(errors){
48095         if(errors instanceof Array){
48096             for(var i = 0, len = errors.length; i < len; i++){
48097                 var fieldError = errors[i];
48098                 var f = this.findField(fieldError.id);
48099                 if(f){
48100                     f.markInvalid(fieldError.msg);
48101                 }
48102             }
48103         }else{
48104             var field, id;
48105             for(id in errors){
48106                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48107                     field.markInvalid(errors[id]);
48108                 }
48109             }
48110         }
48111         Roo.each(this.childForms || [], function (f) {
48112             f.markInvalid(errors);
48113         });
48114         
48115         return this;
48116     },
48117
48118     /**
48119      * Set values for fields in this form in bulk.
48120      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48121      * @return {BasicForm} this
48122      */
48123     setValues : function(values){
48124         if(values instanceof Array){ // array of objects
48125             for(var i = 0, len = values.length; i < len; i++){
48126                 var v = values[i];
48127                 var f = this.findField(v.id);
48128                 if(f){
48129                     f.setValue(v.value);
48130                     if(this.trackResetOnLoad){
48131                         f.originalValue = f.getValue();
48132                     }
48133                 }
48134             }
48135         }else{ // object hash
48136             var field, id;
48137             for(id in values){
48138                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48139                     
48140                     if (field.setFromData && 
48141                         field.valueField && 
48142                         field.displayField &&
48143                         // combos' with local stores can 
48144                         // be queried via setValue()
48145                         // to set their value..
48146                         (field.store && !field.store.isLocal)
48147                         ) {
48148                         // it's a combo
48149                         var sd = { };
48150                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48151                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48152                         field.setFromData(sd);
48153                         
48154                     } else {
48155                         field.setValue(values[id]);
48156                     }
48157                     
48158                     
48159                     if(this.trackResetOnLoad){
48160                         field.originalValue = field.getValue();
48161                     }
48162                 }
48163             }
48164         }
48165         this.resetHasChanged();
48166         
48167         
48168         Roo.each(this.childForms || [], function (f) {
48169             f.setValues(values);
48170             f.resetHasChanged();
48171         });
48172                 
48173         return this;
48174     },
48175  
48176     /**
48177      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48178      * they are returned as an array.
48179      * @param {Boolean} asString
48180      * @return {Object}
48181      */
48182     getValues : function(asString){
48183         if (this.childForms) {
48184             // copy values from the child forms
48185             Roo.each(this.childForms, function (f) {
48186                 this.setValues(f.getValues());
48187             }, this);
48188         }
48189         
48190         // use formdata
48191         if (typeof(FormData) != 'undefined' && asString !== true) {
48192             // this relies on a 'recent' version of chrome apparently...
48193             try {
48194                 var fd = (new FormData(this.el.dom)).entries();
48195                 var ret = {};
48196                 var ent = fd.next();
48197                 while (!ent.done) {
48198                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48199                     ent = fd.next();
48200                 };
48201                 return ret;
48202             } catch(e) {
48203                 
48204             }
48205             
48206         }
48207         
48208         
48209         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48210         if(asString === true){
48211             return fs;
48212         }
48213         return Roo.urlDecode(fs);
48214     },
48215     
48216     /**
48217      * Returns the fields in this form as an object with key/value pairs. 
48218      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48219      * @return {Object}
48220      */
48221     getFieldValues : function(with_hidden)
48222     {
48223         if (this.childForms) {
48224             // copy values from the child forms
48225             // should this call getFieldValues - probably not as we do not currently copy
48226             // hidden fields when we generate..
48227             Roo.each(this.childForms, function (f) {
48228                 this.setValues(f.getValues());
48229             }, this);
48230         }
48231         
48232         var ret = {};
48233         this.items.each(function(f){
48234             if (!f.getName()) {
48235                 return;
48236             }
48237             var v = f.getValue();
48238             if (f.inputType =='radio') {
48239                 if (typeof(ret[f.getName()]) == 'undefined') {
48240                     ret[f.getName()] = ''; // empty..
48241                 }
48242                 
48243                 if (!f.el.dom.checked) {
48244                     return;
48245                     
48246                 }
48247                 v = f.el.dom.value;
48248                 
48249             }
48250             
48251             // not sure if this supported any more..
48252             if ((typeof(v) == 'object') && f.getRawValue) {
48253                 v = f.getRawValue() ; // dates..
48254             }
48255             // combo boxes where name != hiddenName...
48256             if (f.name != f.getName()) {
48257                 ret[f.name] = f.getRawValue();
48258             }
48259             ret[f.getName()] = v;
48260         });
48261         
48262         return ret;
48263     },
48264
48265     /**
48266      * Clears all invalid messages in this form.
48267      * @return {BasicForm} this
48268      */
48269     clearInvalid : function(){
48270         this.items.each(function(f){
48271            f.clearInvalid();
48272         });
48273         
48274         Roo.each(this.childForms || [], function (f) {
48275             f.clearInvalid();
48276         });
48277         
48278         
48279         return this;
48280     },
48281
48282     /**
48283      * Resets this form.
48284      * @return {BasicForm} this
48285      */
48286     reset : function(){
48287         this.items.each(function(f){
48288             f.reset();
48289         });
48290         
48291         Roo.each(this.childForms || [], function (f) {
48292             f.reset();
48293         });
48294         this.resetHasChanged();
48295         
48296         return this;
48297     },
48298
48299     /**
48300      * Add Roo.form components to this form.
48301      * @param {Field} field1
48302      * @param {Field} field2 (optional)
48303      * @param {Field} etc (optional)
48304      * @return {BasicForm} this
48305      */
48306     add : function(){
48307         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48308         return this;
48309     },
48310
48311
48312     /**
48313      * Removes a field from the items collection (does NOT remove its markup).
48314      * @param {Field} field
48315      * @return {BasicForm} this
48316      */
48317     remove : function(field){
48318         this.items.remove(field);
48319         return this;
48320     },
48321
48322     /**
48323      * Looks at the fields in this form, checks them for an id attribute,
48324      * and calls applyTo on the existing dom element with that id.
48325      * @return {BasicForm} this
48326      */
48327     render : function(){
48328         this.items.each(function(f){
48329             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48330                 f.applyTo(f.id);
48331             }
48332         });
48333         return this;
48334     },
48335
48336     /**
48337      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48338      * @param {Object} values
48339      * @return {BasicForm} this
48340      */
48341     applyToFields : function(o){
48342         this.items.each(function(f){
48343            Roo.apply(f, o);
48344         });
48345         return this;
48346     },
48347
48348     /**
48349      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48350      * @param {Object} values
48351      * @return {BasicForm} this
48352      */
48353     applyIfToFields : function(o){
48354         this.items.each(function(f){
48355            Roo.applyIf(f, o);
48356         });
48357         return this;
48358     }
48359 });
48360
48361 // back compat
48362 Roo.BasicForm = Roo.form.BasicForm;
48363
48364 Roo.apply(Roo.form.BasicForm, {
48365     
48366     popover : {
48367         
48368         padding : 5,
48369         
48370         isApplied : false,
48371         
48372         isMasked : false,
48373         
48374         form : false,
48375         
48376         target : false,
48377         
48378         intervalID : false,
48379         
48380         maskEl : false,
48381         
48382         apply : function()
48383         {
48384             if(this.isApplied){
48385                 return;
48386             }
48387             
48388             this.maskEl = {
48389                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48390                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48391                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48392                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48393             };
48394             
48395             this.maskEl.top.enableDisplayMode("block");
48396             this.maskEl.left.enableDisplayMode("block");
48397             this.maskEl.bottom.enableDisplayMode("block");
48398             this.maskEl.right.enableDisplayMode("block");
48399             
48400             Roo.get(document.body).on('click', function(){
48401                 this.unmask();
48402             }, this);
48403             
48404             Roo.get(document.body).on('touchstart', function(){
48405                 this.unmask();
48406             }, this);
48407             
48408             this.isApplied = true
48409         },
48410         
48411         mask : function(form, target)
48412         {
48413             this.form = form;
48414             
48415             this.target = target;
48416             
48417             if(!this.form.errorMask || !target.el){
48418                 return;
48419             }
48420             
48421             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48422             
48423             var ot = this.target.el.calcOffsetsTo(scrollable);
48424             
48425             var scrollTo = ot[1] - this.form.maskOffset;
48426             
48427             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48428             
48429             scrollable.scrollTo('top', scrollTo);
48430             
48431             var el = this.target.wrap || this.target.el;
48432             
48433             var box = el.getBox();
48434             
48435             this.maskEl.top.setStyle('position', 'absolute');
48436             this.maskEl.top.setStyle('z-index', 10000);
48437             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48438             this.maskEl.top.setLeft(0);
48439             this.maskEl.top.setTop(0);
48440             this.maskEl.top.show();
48441             
48442             this.maskEl.left.setStyle('position', 'absolute');
48443             this.maskEl.left.setStyle('z-index', 10000);
48444             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48445             this.maskEl.left.setLeft(0);
48446             this.maskEl.left.setTop(box.y - this.padding);
48447             this.maskEl.left.show();
48448
48449             this.maskEl.bottom.setStyle('position', 'absolute');
48450             this.maskEl.bottom.setStyle('z-index', 10000);
48451             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48452             this.maskEl.bottom.setLeft(0);
48453             this.maskEl.bottom.setTop(box.bottom + this.padding);
48454             this.maskEl.bottom.show();
48455
48456             this.maskEl.right.setStyle('position', 'absolute');
48457             this.maskEl.right.setStyle('z-index', 10000);
48458             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48459             this.maskEl.right.setLeft(box.right + this.padding);
48460             this.maskEl.right.setTop(box.y - this.padding);
48461             this.maskEl.right.show();
48462
48463             this.intervalID = window.setInterval(function() {
48464                 Roo.form.BasicForm.popover.unmask();
48465             }, 10000);
48466
48467             window.onwheel = function(){ return false;};
48468             
48469             (function(){ this.isMasked = true; }).defer(500, this);
48470             
48471         },
48472         
48473         unmask : function()
48474         {
48475             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48476                 return;
48477             }
48478             
48479             this.maskEl.top.setStyle('position', 'absolute');
48480             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48481             this.maskEl.top.hide();
48482
48483             this.maskEl.left.setStyle('position', 'absolute');
48484             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48485             this.maskEl.left.hide();
48486
48487             this.maskEl.bottom.setStyle('position', 'absolute');
48488             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48489             this.maskEl.bottom.hide();
48490
48491             this.maskEl.right.setStyle('position', 'absolute');
48492             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48493             this.maskEl.right.hide();
48494             
48495             window.onwheel = function(){ return true;};
48496             
48497             if(this.intervalID){
48498                 window.clearInterval(this.intervalID);
48499                 this.intervalID = false;
48500             }
48501             
48502             this.isMasked = false;
48503             
48504         }
48505         
48506     }
48507     
48508 });/*
48509  * Based on:
48510  * Ext JS Library 1.1.1
48511  * Copyright(c) 2006-2007, Ext JS, LLC.
48512  *
48513  * Originally Released Under LGPL - original licence link has changed is not relivant.
48514  *
48515  * Fork - LGPL
48516  * <script type="text/javascript">
48517  */
48518
48519 /**
48520  * @class Roo.form.Form
48521  * @extends Roo.form.BasicForm
48522  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
48523  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48524  * @constructor
48525  * @param {Object} config Configuration options
48526  */
48527 Roo.form.Form = function(config){
48528     var xitems =  [];
48529     if (config.items) {
48530         xitems = config.items;
48531         delete config.items;
48532     }
48533    
48534     
48535     Roo.form.Form.superclass.constructor.call(this, null, config);
48536     this.url = this.url || this.action;
48537     if(!this.root){
48538         this.root = new Roo.form.Layout(Roo.applyIf({
48539             id: Roo.id()
48540         }, config));
48541     }
48542     this.active = this.root;
48543     /**
48544      * Array of all the buttons that have been added to this form via {@link addButton}
48545      * @type Array
48546      */
48547     this.buttons = [];
48548     this.allItems = [];
48549     this.addEvents({
48550         /**
48551          * @event clientvalidation
48552          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48553          * @param {Form} this
48554          * @param {Boolean} valid true if the form has passed client-side validation
48555          */
48556         clientvalidation: true,
48557         /**
48558          * @event rendered
48559          * Fires when the form is rendered
48560          * @param {Roo.form.Form} form
48561          */
48562         rendered : true
48563     });
48564     
48565     if (this.progressUrl) {
48566             // push a hidden field onto the list of fields..
48567             this.addxtype( {
48568                     xns: Roo.form, 
48569                     xtype : 'Hidden', 
48570                     name : 'UPLOAD_IDENTIFIER' 
48571             });
48572         }
48573         
48574     
48575     Roo.each(xitems, this.addxtype, this);
48576     
48577 };
48578
48579 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48580      /**
48581      * @cfg {Roo.Button} buttons[] buttons at bottom of form
48582      */
48583     
48584     /**
48585      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48586      */
48587     /**
48588      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48589      */
48590     /**
48591      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48592      */
48593     buttonAlign:'center',
48594
48595     /**
48596      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48597      */
48598     minButtonWidth:75,
48599
48600     /**
48601      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48602      * This property cascades to child containers if not set.
48603      */
48604     labelAlign:'left',
48605
48606     /**
48607      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48608      * fires a looping event with that state. This is required to bind buttons to the valid
48609      * state using the config value formBind:true on the button.
48610      */
48611     monitorValid : false,
48612
48613     /**
48614      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48615      */
48616     monitorPoll : 200,
48617     
48618     /**
48619      * @cfg {String} progressUrl - Url to return progress data 
48620      */
48621     
48622     progressUrl : false,
48623     /**
48624      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48625      * sending a formdata with extra parameters - eg uploaded elements.
48626      */
48627     
48628     formData : false,
48629     
48630     /**
48631      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48632      * fields are added and the column is closed. If no fields are passed the column remains open
48633      * until end() is called.
48634      * @param {Object} config The config to pass to the column
48635      * @param {Field} field1 (optional)
48636      * @param {Field} field2 (optional)
48637      * @param {Field} etc (optional)
48638      * @return Column The column container object
48639      */
48640     column : function(c){
48641         var col = new Roo.form.Column(c);
48642         this.start(col);
48643         if(arguments.length > 1){ // duplicate code required because of Opera
48644             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48645             this.end();
48646         }
48647         return col;
48648     },
48649
48650     /**
48651      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48652      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48653      * until end() is called.
48654      * @param {Object} config The config to pass to the fieldset
48655      * @param {Field} field1 (optional)
48656      * @param {Field} field2 (optional)
48657      * @param {Field} etc (optional)
48658      * @return FieldSet The fieldset container object
48659      */
48660     fieldset : function(c){
48661         var fs = new Roo.form.FieldSet(c);
48662         this.start(fs);
48663         if(arguments.length > 1){ // duplicate code required because of Opera
48664             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48665             this.end();
48666         }
48667         return fs;
48668     },
48669
48670     /**
48671      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48672      * fields are added and the container is closed. If no fields are passed the container remains open
48673      * until end() is called.
48674      * @param {Object} config The config to pass to the Layout
48675      * @param {Field} field1 (optional)
48676      * @param {Field} field2 (optional)
48677      * @param {Field} etc (optional)
48678      * @return Layout The container object
48679      */
48680     container : function(c){
48681         var l = new Roo.form.Layout(c);
48682         this.start(l);
48683         if(arguments.length > 1){ // duplicate code required because of Opera
48684             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48685             this.end();
48686         }
48687         return l;
48688     },
48689
48690     /**
48691      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48692      * @param {Object} container A Roo.form.Layout or subclass of Layout
48693      * @return {Form} this
48694      */
48695     start : function(c){
48696         // cascade label info
48697         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48698         this.active.stack.push(c);
48699         c.ownerCt = this.active;
48700         this.active = c;
48701         return this;
48702     },
48703
48704     /**
48705      * Closes the current open container
48706      * @return {Form} this
48707      */
48708     end : function(){
48709         if(this.active == this.root){
48710             return this;
48711         }
48712         this.active = this.active.ownerCt;
48713         return this;
48714     },
48715
48716     /**
48717      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48718      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48719      * as the label of the field.
48720      * @param {Field} field1
48721      * @param {Field} field2 (optional)
48722      * @param {Field} etc. (optional)
48723      * @return {Form} this
48724      */
48725     add : function(){
48726         this.active.stack.push.apply(this.active.stack, arguments);
48727         this.allItems.push.apply(this.allItems,arguments);
48728         var r = [];
48729         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48730             if(a[i].isFormField){
48731                 r.push(a[i]);
48732             }
48733         }
48734         if(r.length > 0){
48735             Roo.form.Form.superclass.add.apply(this, r);
48736         }
48737         return this;
48738     },
48739     
48740
48741     
48742     
48743     
48744      /**
48745      * Find any element that has been added to a form, using it's ID or name
48746      * This can include framesets, columns etc. along with regular fields..
48747      * @param {String} id - id or name to find.
48748      
48749      * @return {Element} e - or false if nothing found.
48750      */
48751     findbyId : function(id)
48752     {
48753         var ret = false;
48754         if (!id) {
48755             return ret;
48756         }
48757         Roo.each(this.allItems, function(f){
48758             if (f.id == id || f.name == id ){
48759                 ret = f;
48760                 return false;
48761             }
48762         });
48763         return ret;
48764     },
48765
48766     
48767     
48768     /**
48769      * Render this form into the passed container. This should only be called once!
48770      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48771      * @return {Form} this
48772      */
48773     render : function(ct)
48774     {
48775         
48776         
48777         
48778         ct = Roo.get(ct);
48779         var o = this.autoCreate || {
48780             tag: 'form',
48781             method : this.method || 'POST',
48782             id : this.id || Roo.id()
48783         };
48784         this.initEl(ct.createChild(o));
48785
48786         this.root.render(this.el);
48787         
48788        
48789              
48790         this.items.each(function(f){
48791             f.render('x-form-el-'+f.id);
48792         });
48793
48794         if(this.buttons.length > 0){
48795             // tables are required to maintain order and for correct IE layout
48796             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48797                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48798                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48799             }}, null, true);
48800             var tr = tb.getElementsByTagName('tr')[0];
48801             for(var i = 0, len = this.buttons.length; i < len; i++) {
48802                 var b = this.buttons[i];
48803                 var td = document.createElement('td');
48804                 td.className = 'x-form-btn-td';
48805                 b.render(tr.appendChild(td));
48806             }
48807         }
48808         if(this.monitorValid){ // initialize after render
48809             this.startMonitoring();
48810         }
48811         this.fireEvent('rendered', this);
48812         return this;
48813     },
48814
48815     /**
48816      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48817      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48818      * object or a valid Roo.DomHelper element config
48819      * @param {Function} handler The function called when the button is clicked
48820      * @param {Object} scope (optional) The scope of the handler function
48821      * @return {Roo.Button}
48822      */
48823     addButton : function(config, handler, scope){
48824         var bc = {
48825             handler: handler,
48826             scope: scope,
48827             minWidth: this.minButtonWidth,
48828             hideParent:true
48829         };
48830         if(typeof config == "string"){
48831             bc.text = config;
48832         }else{
48833             Roo.apply(bc, config);
48834         }
48835         var btn = new Roo.Button(null, bc);
48836         this.buttons.push(btn);
48837         return btn;
48838     },
48839
48840      /**
48841      * Adds a series of form elements (using the xtype property as the factory method.
48842      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48843      * @param {Object} config 
48844      */
48845     
48846     addxtype : function()
48847     {
48848         var ar = Array.prototype.slice.call(arguments, 0);
48849         var ret = false;
48850         for(var i = 0; i < ar.length; i++) {
48851             if (!ar[i]) {
48852                 continue; // skip -- if this happends something invalid got sent, we 
48853                 // should ignore it, as basically that interface element will not show up
48854                 // and that should be pretty obvious!!
48855             }
48856             
48857             if (Roo.form[ar[i].xtype]) {
48858                 ar[i].form = this;
48859                 var fe = Roo.factory(ar[i], Roo.form);
48860                 if (!ret) {
48861                     ret = fe;
48862                 }
48863                 fe.form = this;
48864                 if (fe.store) {
48865                     fe.store.form = this;
48866                 }
48867                 if (fe.isLayout) {  
48868                          
48869                     this.start(fe);
48870                     this.allItems.push(fe);
48871                     if (fe.items && fe.addxtype) {
48872                         fe.addxtype.apply(fe, fe.items);
48873                         delete fe.items;
48874                     }
48875                      this.end();
48876                     continue;
48877                 }
48878                 
48879                 
48880                  
48881                 this.add(fe);
48882               //  console.log('adding ' + ar[i].xtype);
48883             }
48884             if (ar[i].xtype == 'Button') {  
48885                 //console.log('adding button');
48886                 //console.log(ar[i]);
48887                 this.addButton(ar[i]);
48888                 this.allItems.push(fe);
48889                 continue;
48890             }
48891             
48892             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48893                 alert('end is not supported on xtype any more, use items');
48894             //    this.end();
48895             //    //console.log('adding end');
48896             }
48897             
48898         }
48899         return ret;
48900     },
48901     
48902     /**
48903      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48904      * option "monitorValid"
48905      */
48906     startMonitoring : function(){
48907         if(!this.bound){
48908             this.bound = true;
48909             Roo.TaskMgr.start({
48910                 run : this.bindHandler,
48911                 interval : this.monitorPoll || 200,
48912                 scope: this
48913             });
48914         }
48915     },
48916
48917     /**
48918      * Stops monitoring of the valid state of this form
48919      */
48920     stopMonitoring : function(){
48921         this.bound = false;
48922     },
48923
48924     // private
48925     bindHandler : function(){
48926         if(!this.bound){
48927             return false; // stops binding
48928         }
48929         var valid = true;
48930         this.items.each(function(f){
48931             if(!f.isValid(true)){
48932                 valid = false;
48933                 return false;
48934             }
48935         });
48936         for(var i = 0, len = this.buttons.length; i < len; i++){
48937             var btn = this.buttons[i];
48938             if(btn.formBind === true && btn.disabled === valid){
48939                 btn.setDisabled(!valid);
48940             }
48941         }
48942         this.fireEvent('clientvalidation', this, valid);
48943     }
48944     
48945     
48946     
48947     
48948     
48949     
48950     
48951     
48952 });
48953
48954
48955 // back compat
48956 Roo.Form = Roo.form.Form;
48957 /*
48958  * Based on:
48959  * Ext JS Library 1.1.1
48960  * Copyright(c) 2006-2007, Ext JS, LLC.
48961  *
48962  * Originally Released Under LGPL - original licence link has changed is not relivant.
48963  *
48964  * Fork - LGPL
48965  * <script type="text/javascript">
48966  */
48967
48968 // as we use this in bootstrap.
48969 Roo.namespace('Roo.form');
48970  /**
48971  * @class Roo.form.Action
48972  * Internal Class used to handle form actions
48973  * @constructor
48974  * @param {Roo.form.BasicForm} el The form element or its id
48975  * @param {Object} config Configuration options
48976  */
48977
48978  
48979  
48980 // define the action interface
48981 Roo.form.Action = function(form, options){
48982     this.form = form;
48983     this.options = options || {};
48984 };
48985 /**
48986  * Client Validation Failed
48987  * @const 
48988  */
48989 Roo.form.Action.CLIENT_INVALID = 'client';
48990 /**
48991  * Server Validation Failed
48992  * @const 
48993  */
48994 Roo.form.Action.SERVER_INVALID = 'server';
48995  /**
48996  * Connect to Server Failed
48997  * @const 
48998  */
48999 Roo.form.Action.CONNECT_FAILURE = 'connect';
49000 /**
49001  * Reading Data from Server Failed
49002  * @const 
49003  */
49004 Roo.form.Action.LOAD_FAILURE = 'load';
49005
49006 Roo.form.Action.prototype = {
49007     type : 'default',
49008     failureType : undefined,
49009     response : undefined,
49010     result : undefined,
49011
49012     // interface method
49013     run : function(options){
49014
49015     },
49016
49017     // interface method
49018     success : function(response){
49019
49020     },
49021
49022     // interface method
49023     handleResponse : function(response){
49024
49025     },
49026
49027     // default connection failure
49028     failure : function(response){
49029         
49030         this.response = response;
49031         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49032         this.form.afterAction(this, false);
49033     },
49034
49035     processResponse : function(response){
49036         this.response = response;
49037         if(!response.responseText){
49038             return true;
49039         }
49040         this.result = this.handleResponse(response);
49041         return this.result;
49042     },
49043
49044     // utility functions used internally
49045     getUrl : function(appendParams){
49046         var url = this.options.url || this.form.url || this.form.el.dom.action;
49047         if(appendParams){
49048             var p = this.getParams();
49049             if(p){
49050                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49051             }
49052         }
49053         return url;
49054     },
49055
49056     getMethod : function(){
49057         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49058     },
49059
49060     getParams : function(){
49061         var bp = this.form.baseParams;
49062         var p = this.options.params;
49063         if(p){
49064             if(typeof p == "object"){
49065                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49066             }else if(typeof p == 'string' && bp){
49067                 p += '&' + Roo.urlEncode(bp);
49068             }
49069         }else if(bp){
49070             p = Roo.urlEncode(bp);
49071         }
49072         return p;
49073     },
49074
49075     createCallback : function(){
49076         return {
49077             success: this.success,
49078             failure: this.failure,
49079             scope: this,
49080             timeout: (this.form.timeout*1000),
49081             upload: this.form.fileUpload ? this.success : undefined
49082         };
49083     }
49084 };
49085
49086 Roo.form.Action.Submit = function(form, options){
49087     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49088 };
49089
49090 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49091     type : 'submit',
49092
49093     haveProgress : false,
49094     uploadComplete : false,
49095     
49096     // uploadProgress indicator.
49097     uploadProgress : function()
49098     {
49099         if (!this.form.progressUrl) {
49100             return;
49101         }
49102         
49103         if (!this.haveProgress) {
49104             Roo.MessageBox.progress("Uploading", "Uploading");
49105         }
49106         if (this.uploadComplete) {
49107            Roo.MessageBox.hide();
49108            return;
49109         }
49110         
49111         this.haveProgress = true;
49112    
49113         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49114         
49115         var c = new Roo.data.Connection();
49116         c.request({
49117             url : this.form.progressUrl,
49118             params: {
49119                 id : uid
49120             },
49121             method: 'GET',
49122             success : function(req){
49123                //console.log(data);
49124                 var rdata = false;
49125                 var edata;
49126                 try  {
49127                    rdata = Roo.decode(req.responseText)
49128                 } catch (e) {
49129                     Roo.log("Invalid data from server..");
49130                     Roo.log(edata);
49131                     return;
49132                 }
49133                 if (!rdata || !rdata.success) {
49134                     Roo.log(rdata);
49135                     Roo.MessageBox.alert(Roo.encode(rdata));
49136                     return;
49137                 }
49138                 var data = rdata.data;
49139                 
49140                 if (this.uploadComplete) {
49141                    Roo.MessageBox.hide();
49142                    return;
49143                 }
49144                    
49145                 if (data){
49146                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49147                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49148                     );
49149                 }
49150                 this.uploadProgress.defer(2000,this);
49151             },
49152        
49153             failure: function(data) {
49154                 Roo.log('progress url failed ');
49155                 Roo.log(data);
49156             },
49157             scope : this
49158         });
49159            
49160     },
49161     
49162     
49163     run : function()
49164     {
49165         // run get Values on the form, so it syncs any secondary forms.
49166         this.form.getValues();
49167         
49168         var o = this.options;
49169         var method = this.getMethod();
49170         var isPost = method == 'POST';
49171         if(o.clientValidation === false || this.form.isValid()){
49172             
49173             if (this.form.progressUrl) {
49174                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49175                     (new Date() * 1) + '' + Math.random());
49176                     
49177             } 
49178             
49179             
49180             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49181                 form:this.form.el.dom,
49182                 url:this.getUrl(!isPost),
49183                 method: method,
49184                 params:isPost ? this.getParams() : null,
49185                 isUpload: this.form.fileUpload,
49186                 formData : this.form.formData
49187             }));
49188             
49189             this.uploadProgress();
49190
49191         }else if (o.clientValidation !== false){ // client validation failed
49192             this.failureType = Roo.form.Action.CLIENT_INVALID;
49193             this.form.afterAction(this, false);
49194         }
49195     },
49196
49197     success : function(response)
49198     {
49199         this.uploadComplete= true;
49200         if (this.haveProgress) {
49201             Roo.MessageBox.hide();
49202         }
49203         
49204         
49205         var result = this.processResponse(response);
49206         if(result === true || result.success){
49207             this.form.afterAction(this, true);
49208             return;
49209         }
49210         if(result.errors){
49211             this.form.markInvalid(result.errors);
49212             this.failureType = Roo.form.Action.SERVER_INVALID;
49213         }
49214         this.form.afterAction(this, false);
49215     },
49216     failure : function(response)
49217     {
49218         this.uploadComplete= true;
49219         if (this.haveProgress) {
49220             Roo.MessageBox.hide();
49221         }
49222         
49223         this.response = response;
49224         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49225         this.form.afterAction(this, false);
49226     },
49227     
49228     handleResponse : function(response){
49229         if(this.form.errorReader){
49230             var rs = this.form.errorReader.read(response);
49231             var errors = [];
49232             if(rs.records){
49233                 for(var i = 0, len = rs.records.length; i < len; i++) {
49234                     var r = rs.records[i];
49235                     errors[i] = r.data;
49236                 }
49237             }
49238             if(errors.length < 1){
49239                 errors = null;
49240             }
49241             return {
49242                 success : rs.success,
49243                 errors : errors
49244             };
49245         }
49246         var ret = false;
49247         try {
49248             ret = Roo.decode(response.responseText);
49249         } catch (e) {
49250             ret = {
49251                 success: false,
49252                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49253                 errors : []
49254             };
49255         }
49256         return ret;
49257         
49258     }
49259 });
49260
49261
49262 Roo.form.Action.Load = function(form, options){
49263     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49264     this.reader = this.form.reader;
49265 };
49266
49267 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49268     type : 'load',
49269
49270     run : function(){
49271         
49272         Roo.Ajax.request(Roo.apply(
49273                 this.createCallback(), {
49274                     method:this.getMethod(),
49275                     url:this.getUrl(false),
49276                     params:this.getParams()
49277         }));
49278     },
49279
49280     success : function(response){
49281         
49282         var result = this.processResponse(response);
49283         if(result === true || !result.success || !result.data){
49284             this.failureType = Roo.form.Action.LOAD_FAILURE;
49285             this.form.afterAction(this, false);
49286             return;
49287         }
49288         this.form.clearInvalid();
49289         this.form.setValues(result.data);
49290         this.form.afterAction(this, true);
49291     },
49292
49293     handleResponse : function(response){
49294         if(this.form.reader){
49295             var rs = this.form.reader.read(response);
49296             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49297             return {
49298                 success : rs.success,
49299                 data : data
49300             };
49301         }
49302         return Roo.decode(response.responseText);
49303     }
49304 });
49305
49306 Roo.form.Action.ACTION_TYPES = {
49307     'load' : Roo.form.Action.Load,
49308     'submit' : Roo.form.Action.Submit
49309 };/*
49310  * Based on:
49311  * Ext JS Library 1.1.1
49312  * Copyright(c) 2006-2007, Ext JS, LLC.
49313  *
49314  * Originally Released Under LGPL - original licence link has changed is not relivant.
49315  *
49316  * Fork - LGPL
49317  * <script type="text/javascript">
49318  */
49319  
49320 /**
49321  * @class Roo.form.Layout
49322  * @extends Roo.Component
49323  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49324  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49325  * @constructor
49326  * @param {Object} config Configuration options
49327  */
49328 Roo.form.Layout = function(config){
49329     var xitems = [];
49330     if (config.items) {
49331         xitems = config.items;
49332         delete config.items;
49333     }
49334     Roo.form.Layout.superclass.constructor.call(this, config);
49335     this.stack = [];
49336     Roo.each(xitems, this.addxtype, this);
49337      
49338 };
49339
49340 Roo.extend(Roo.form.Layout, Roo.Component, {
49341     /**
49342      * @cfg {String/Object} autoCreate
49343      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49344      */
49345     /**
49346      * @cfg {String/Object/Function} style
49347      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49348      * a function which returns such a specification.
49349      */
49350     /**
49351      * @cfg {String} labelAlign
49352      * Valid values are "left," "top" and "right" (defaults to "left")
49353      */
49354     /**
49355      * @cfg {Number} labelWidth
49356      * Fixed width in pixels of all field labels (defaults to undefined)
49357      */
49358     /**
49359      * @cfg {Boolean} clear
49360      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49361      */
49362     clear : true,
49363     /**
49364      * @cfg {String} labelSeparator
49365      * The separator to use after field labels (defaults to ':')
49366      */
49367     labelSeparator : ':',
49368     /**
49369      * @cfg {Boolean} hideLabels
49370      * True to suppress the display of field labels in this layout (defaults to false)
49371      */
49372     hideLabels : false,
49373
49374     // private
49375     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49376     
49377     isLayout : true,
49378     
49379     // private
49380     onRender : function(ct, position){
49381         if(this.el){ // from markup
49382             this.el = Roo.get(this.el);
49383         }else {  // generate
49384             var cfg = this.getAutoCreate();
49385             this.el = ct.createChild(cfg, position);
49386         }
49387         if(this.style){
49388             this.el.applyStyles(this.style);
49389         }
49390         if(this.labelAlign){
49391             this.el.addClass('x-form-label-'+this.labelAlign);
49392         }
49393         if(this.hideLabels){
49394             this.labelStyle = "display:none";
49395             this.elementStyle = "padding-left:0;";
49396         }else{
49397             if(typeof this.labelWidth == 'number'){
49398                 this.labelStyle = "width:"+this.labelWidth+"px;";
49399                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49400             }
49401             if(this.labelAlign == 'top'){
49402                 this.labelStyle = "width:auto;";
49403                 this.elementStyle = "padding-left:0;";
49404             }
49405         }
49406         var stack = this.stack;
49407         var slen = stack.length;
49408         if(slen > 0){
49409             if(!this.fieldTpl){
49410                 var t = new Roo.Template(
49411                     '<div class="x-form-item {5}">',
49412                         '<label for="{0}" style="{2}">{1}{4}</label>',
49413                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49414                         '</div>',
49415                     '</div><div class="x-form-clear-left"></div>'
49416                 );
49417                 t.disableFormats = true;
49418                 t.compile();
49419                 Roo.form.Layout.prototype.fieldTpl = t;
49420             }
49421             for(var i = 0; i < slen; i++) {
49422                 if(stack[i].isFormField){
49423                     this.renderField(stack[i]);
49424                 }else{
49425                     this.renderComponent(stack[i]);
49426                 }
49427             }
49428         }
49429         if(this.clear){
49430             this.el.createChild({cls:'x-form-clear'});
49431         }
49432     },
49433
49434     // private
49435     renderField : function(f){
49436         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49437                f.id, //0
49438                f.fieldLabel, //1
49439                f.labelStyle||this.labelStyle||'', //2
49440                this.elementStyle||'', //3
49441                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49442                f.itemCls||this.itemCls||''  //5
49443        ], true).getPrevSibling());
49444     },
49445
49446     // private
49447     renderComponent : function(c){
49448         c.render(c.isLayout ? this.el : this.el.createChild());    
49449     },
49450     /**
49451      * Adds a object form elements (using the xtype property as the factory method.)
49452      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49453      * @param {Object} config 
49454      */
49455     addxtype : function(o)
49456     {
49457         // create the lement.
49458         o.form = this.form;
49459         var fe = Roo.factory(o, Roo.form);
49460         this.form.allItems.push(fe);
49461         this.stack.push(fe);
49462         
49463         if (fe.isFormField) {
49464             this.form.items.add(fe);
49465         }
49466          
49467         return fe;
49468     }
49469 });
49470
49471 /**
49472  * @class Roo.form.Column
49473  * @extends Roo.form.Layout
49474  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49475  * @constructor
49476  * @param {Object} config Configuration options
49477  */
49478 Roo.form.Column = function(config){
49479     Roo.form.Column.superclass.constructor.call(this, config);
49480 };
49481
49482 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49483     /**
49484      * @cfg {Number/String} width
49485      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49486      */
49487     /**
49488      * @cfg {String/Object} autoCreate
49489      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49490      */
49491
49492     // private
49493     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49494
49495     // private
49496     onRender : function(ct, position){
49497         Roo.form.Column.superclass.onRender.call(this, ct, position);
49498         if(this.width){
49499             this.el.setWidth(this.width);
49500         }
49501     }
49502 });
49503
49504
49505 /**
49506  * @class Roo.form.Row
49507  * @extends Roo.form.Layout
49508  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49509  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49510  * @constructor
49511  * @param {Object} config Configuration options
49512  */
49513
49514  
49515 Roo.form.Row = function(config){
49516     Roo.form.Row.superclass.constructor.call(this, config);
49517 };
49518  
49519 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49520       /**
49521      * @cfg {Number/String} width
49522      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49523      */
49524     /**
49525      * @cfg {Number/String} height
49526      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49527      */
49528     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49529     
49530     padWidth : 20,
49531     // private
49532     onRender : function(ct, position){
49533         //console.log('row render');
49534         if(!this.rowTpl){
49535             var t = new Roo.Template(
49536                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49537                     '<label for="{0}" style="{2}">{1}{4}</label>',
49538                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49539                     '</div>',
49540                 '</div>'
49541             );
49542             t.disableFormats = true;
49543             t.compile();
49544             Roo.form.Layout.prototype.rowTpl = t;
49545         }
49546         this.fieldTpl = this.rowTpl;
49547         
49548         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49549         var labelWidth = 100;
49550         
49551         if ((this.labelAlign != 'top')) {
49552             if (typeof this.labelWidth == 'number') {
49553                 labelWidth = this.labelWidth
49554             }
49555             this.padWidth =  20 + labelWidth;
49556             
49557         }
49558         
49559         Roo.form.Column.superclass.onRender.call(this, ct, position);
49560         if(this.width){
49561             this.el.setWidth(this.width);
49562         }
49563         if(this.height){
49564             this.el.setHeight(this.height);
49565         }
49566     },
49567     
49568     // private
49569     renderField : function(f){
49570         f.fieldEl = this.fieldTpl.append(this.el, [
49571                f.id, f.fieldLabel,
49572                f.labelStyle||this.labelStyle||'',
49573                this.elementStyle||'',
49574                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49575                f.itemCls||this.itemCls||'',
49576                f.width ? f.width + this.padWidth : 160 + this.padWidth
49577        ],true);
49578     }
49579 });
49580  
49581
49582 /**
49583  * @class Roo.form.FieldSet
49584  * @extends Roo.form.Layout
49585  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49586  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49587  * @constructor
49588  * @param {Object} config Configuration options
49589  */
49590 Roo.form.FieldSet = function(config){
49591     Roo.form.FieldSet.superclass.constructor.call(this, config);
49592 };
49593
49594 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49595     /**
49596      * @cfg {String} legend
49597      * The text to display as the legend for the FieldSet (defaults to '')
49598      */
49599     /**
49600      * @cfg {String/Object} autoCreate
49601      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49602      */
49603
49604     // private
49605     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49606
49607     // private
49608     onRender : function(ct, position){
49609         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49610         if(this.legend){
49611             this.setLegend(this.legend);
49612         }
49613     },
49614
49615     // private
49616     setLegend : function(text){
49617         if(this.rendered){
49618             this.el.child('legend').update(text);
49619         }
49620     }
49621 });/*
49622  * Based on:
49623  * Ext JS Library 1.1.1
49624  * Copyright(c) 2006-2007, Ext JS, LLC.
49625  *
49626  * Originally Released Under LGPL - original licence link has changed is not relivant.
49627  *
49628  * Fork - LGPL
49629  * <script type="text/javascript">
49630  */
49631 /**
49632  * @class Roo.form.VTypes
49633  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49634  * @singleton
49635  */
49636 Roo.form.VTypes = function(){
49637     // closure these in so they are only created once.
49638     var alpha = /^[a-zA-Z_]+$/;
49639     var alphanum = /^[a-zA-Z0-9_]+$/;
49640     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49641     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49642
49643     // All these messages and functions are configurable
49644     return {
49645         /**
49646          * The function used to validate email addresses
49647          * @param {String} value The email address
49648          */
49649         'email' : function(v){
49650             return email.test(v);
49651         },
49652         /**
49653          * The error text to display when the email validation function returns false
49654          * @type String
49655          */
49656         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49657         /**
49658          * The keystroke filter mask to be applied on email input
49659          * @type RegExp
49660          */
49661         'emailMask' : /[a-z0-9_\.\-@]/i,
49662
49663         /**
49664          * The function used to validate URLs
49665          * @param {String} value The URL
49666          */
49667         'url' : function(v){
49668             return url.test(v);
49669         },
49670         /**
49671          * The error text to display when the url validation function returns false
49672          * @type String
49673          */
49674         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49675         
49676         /**
49677          * The function used to validate alpha values
49678          * @param {String} value The value
49679          */
49680         'alpha' : function(v){
49681             return alpha.test(v);
49682         },
49683         /**
49684          * The error text to display when the alpha validation function returns false
49685          * @type String
49686          */
49687         'alphaText' : 'This field should only contain letters and _',
49688         /**
49689          * The keystroke filter mask to be applied on alpha input
49690          * @type RegExp
49691          */
49692         'alphaMask' : /[a-z_]/i,
49693
49694         /**
49695          * The function used to validate alphanumeric values
49696          * @param {String} value The value
49697          */
49698         'alphanum' : function(v){
49699             return alphanum.test(v);
49700         },
49701         /**
49702          * The error text to display when the alphanumeric validation function returns false
49703          * @type String
49704          */
49705         'alphanumText' : 'This field should only contain letters, numbers and _',
49706         /**
49707          * The keystroke filter mask to be applied on alphanumeric input
49708          * @type RegExp
49709          */
49710         'alphanumMask' : /[a-z0-9_]/i
49711     };
49712 }();//<script type="text/javascript">
49713
49714 /**
49715  * @class Roo.form.FCKeditor
49716  * @extends Roo.form.TextArea
49717  * Wrapper around the FCKEditor http://www.fckeditor.net
49718  * @constructor
49719  * Creates a new FCKeditor
49720  * @param {Object} config Configuration options
49721  */
49722 Roo.form.FCKeditor = function(config){
49723     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49724     this.addEvents({
49725          /**
49726          * @event editorinit
49727          * Fired when the editor is initialized - you can add extra handlers here..
49728          * @param {FCKeditor} this
49729          * @param {Object} the FCK object.
49730          */
49731         editorinit : true
49732     });
49733     
49734     
49735 };
49736 Roo.form.FCKeditor.editors = { };
49737 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49738 {
49739     //defaultAutoCreate : {
49740     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49741     //},
49742     // private
49743     /**
49744      * @cfg {Object} fck options - see fck manual for details.
49745      */
49746     fckconfig : false,
49747     
49748     /**
49749      * @cfg {Object} fck toolbar set (Basic or Default)
49750      */
49751     toolbarSet : 'Basic',
49752     /**
49753      * @cfg {Object} fck BasePath
49754      */ 
49755     basePath : '/fckeditor/',
49756     
49757     
49758     frame : false,
49759     
49760     value : '',
49761     
49762    
49763     onRender : function(ct, position)
49764     {
49765         if(!this.el){
49766             this.defaultAutoCreate = {
49767                 tag: "textarea",
49768                 style:"width:300px;height:60px;",
49769                 autocomplete: "new-password"
49770             };
49771         }
49772         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49773         /*
49774         if(this.grow){
49775             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49776             if(this.preventScrollbars){
49777                 this.el.setStyle("overflow", "hidden");
49778             }
49779             this.el.setHeight(this.growMin);
49780         }
49781         */
49782         //console.log('onrender' + this.getId() );
49783         Roo.form.FCKeditor.editors[this.getId()] = this;
49784          
49785
49786         this.replaceTextarea() ;
49787         
49788     },
49789     
49790     getEditor : function() {
49791         return this.fckEditor;
49792     },
49793     /**
49794      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49795      * @param {Mixed} value The value to set
49796      */
49797     
49798     
49799     setValue : function(value)
49800     {
49801         //console.log('setValue: ' + value);
49802         
49803         if(typeof(value) == 'undefined') { // not sure why this is happending...
49804             return;
49805         }
49806         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49807         
49808         //if(!this.el || !this.getEditor()) {
49809         //    this.value = value;
49810             //this.setValue.defer(100,this,[value]);    
49811         //    return;
49812         //} 
49813         
49814         if(!this.getEditor()) {
49815             return;
49816         }
49817         
49818         this.getEditor().SetData(value);
49819         
49820         //
49821
49822     },
49823
49824     /**
49825      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49826      * @return {Mixed} value The field value
49827      */
49828     getValue : function()
49829     {
49830         
49831         if (this.frame && this.frame.dom.style.display == 'none') {
49832             return Roo.form.FCKeditor.superclass.getValue.call(this);
49833         }
49834         
49835         if(!this.el || !this.getEditor()) {
49836            
49837            // this.getValue.defer(100,this); 
49838             return this.value;
49839         }
49840        
49841         
49842         var value=this.getEditor().GetData();
49843         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49844         return Roo.form.FCKeditor.superclass.getValue.call(this);
49845         
49846
49847     },
49848
49849     /**
49850      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49851      * @return {Mixed} value The field value
49852      */
49853     getRawValue : function()
49854     {
49855         if (this.frame && this.frame.dom.style.display == 'none') {
49856             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49857         }
49858         
49859         if(!this.el || !this.getEditor()) {
49860             //this.getRawValue.defer(100,this); 
49861             return this.value;
49862             return;
49863         }
49864         
49865         
49866         
49867         var value=this.getEditor().GetData();
49868         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49869         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49870          
49871     },
49872     
49873     setSize : function(w,h) {
49874         
49875         
49876         
49877         //if (this.frame && this.frame.dom.style.display == 'none') {
49878         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49879         //    return;
49880         //}
49881         //if(!this.el || !this.getEditor()) {
49882         //    this.setSize.defer(100,this, [w,h]); 
49883         //    return;
49884         //}
49885         
49886         
49887         
49888         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49889         
49890         this.frame.dom.setAttribute('width', w);
49891         this.frame.dom.setAttribute('height', h);
49892         this.frame.setSize(w,h);
49893         
49894     },
49895     
49896     toggleSourceEdit : function(value) {
49897         
49898       
49899          
49900         this.el.dom.style.display = value ? '' : 'none';
49901         this.frame.dom.style.display = value ?  'none' : '';
49902         
49903     },
49904     
49905     
49906     focus: function(tag)
49907     {
49908         if (this.frame.dom.style.display == 'none') {
49909             return Roo.form.FCKeditor.superclass.focus.call(this);
49910         }
49911         if(!this.el || !this.getEditor()) {
49912             this.focus.defer(100,this, [tag]); 
49913             return;
49914         }
49915         
49916         
49917         
49918         
49919         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49920         this.getEditor().Focus();
49921         if (tgs.length) {
49922             if (!this.getEditor().Selection.GetSelection()) {
49923                 this.focus.defer(100,this, [tag]); 
49924                 return;
49925             }
49926             
49927             
49928             var r = this.getEditor().EditorDocument.createRange();
49929             r.setStart(tgs[0],0);
49930             r.setEnd(tgs[0],0);
49931             this.getEditor().Selection.GetSelection().removeAllRanges();
49932             this.getEditor().Selection.GetSelection().addRange(r);
49933             this.getEditor().Focus();
49934         }
49935         
49936     },
49937     
49938     
49939     
49940     replaceTextarea : function()
49941     {
49942         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49943             return ;
49944         }
49945         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49946         //{
49947             // We must check the elements firstly using the Id and then the name.
49948         var oTextarea = document.getElementById( this.getId() );
49949         
49950         var colElementsByName = document.getElementsByName( this.getId() ) ;
49951          
49952         oTextarea.style.display = 'none' ;
49953
49954         if ( oTextarea.tabIndex ) {            
49955             this.TabIndex = oTextarea.tabIndex ;
49956         }
49957         
49958         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49959         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49960         this.frame = Roo.get(this.getId() + '___Frame')
49961     },
49962     
49963     _getConfigHtml : function()
49964     {
49965         var sConfig = '' ;
49966
49967         for ( var o in this.fckconfig ) {
49968             sConfig += sConfig.length > 0  ? '&amp;' : '';
49969             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49970         }
49971
49972         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49973     },
49974     
49975     
49976     _getIFrameHtml : function()
49977     {
49978         var sFile = 'fckeditor.html' ;
49979         /* no idea what this is about..
49980         try
49981         {
49982             if ( (/fcksource=true/i).test( window.top.location.search ) )
49983                 sFile = 'fckeditor.original.html' ;
49984         }
49985         catch (e) { 
49986         */
49987
49988         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49989         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49990         
49991         
49992         var html = '<iframe id="' + this.getId() +
49993             '___Frame" src="' + sLink +
49994             '" width="' + this.width +
49995             '" height="' + this.height + '"' +
49996             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49997             ' frameborder="0" scrolling="no"></iframe>' ;
49998
49999         return html ;
50000     },
50001     
50002     _insertHtmlBefore : function( html, element )
50003     {
50004         if ( element.insertAdjacentHTML )       {
50005             // IE
50006             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50007         } else { // Gecko
50008             var oRange = document.createRange() ;
50009             oRange.setStartBefore( element ) ;
50010             var oFragment = oRange.createContextualFragment( html );
50011             element.parentNode.insertBefore( oFragment, element ) ;
50012         }
50013     }
50014     
50015     
50016   
50017     
50018     
50019     
50020     
50021
50022 });
50023
50024 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50025
50026 function FCKeditor_OnComplete(editorInstance){
50027     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50028     f.fckEditor = editorInstance;
50029     //console.log("loaded");
50030     f.fireEvent('editorinit', f, editorInstance);
50031
50032   
50033
50034  
50035
50036
50037
50038
50039
50040
50041
50042
50043
50044
50045
50046
50047
50048
50049
50050 //<script type="text/javascript">
50051 /**
50052  * @class Roo.form.GridField
50053  * @extends Roo.form.Field
50054  * Embed a grid (or editable grid into a form)
50055  * STATUS ALPHA
50056  * 
50057  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50058  * it needs 
50059  * xgrid.store = Roo.data.Store
50060  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50061  * xgrid.store.reader = Roo.data.JsonReader 
50062  * 
50063  * 
50064  * @constructor
50065  * Creates a new GridField
50066  * @param {Object} config Configuration options
50067  */
50068 Roo.form.GridField = function(config){
50069     Roo.form.GridField.superclass.constructor.call(this, config);
50070      
50071 };
50072
50073 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50074     /**
50075      * @cfg {Number} width  - used to restrict width of grid..
50076      */
50077     width : 100,
50078     /**
50079      * @cfg {Number} height - used to restrict height of grid..
50080      */
50081     height : 50,
50082      /**
50083      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50084          * 
50085          *}
50086      */
50087     xgrid : false, 
50088     /**
50089      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50090      * {tag: "input", type: "checkbox", autocomplete: "off"})
50091      */
50092    // defaultAutoCreate : { tag: 'div' },
50093     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50094     /**
50095      * @cfg {String} addTitle Text to include for adding a title.
50096      */
50097     addTitle : false,
50098     //
50099     onResize : function(){
50100         Roo.form.Field.superclass.onResize.apply(this, arguments);
50101     },
50102
50103     initEvents : function(){
50104         // Roo.form.Checkbox.superclass.initEvents.call(this);
50105         // has no events...
50106        
50107     },
50108
50109
50110     getResizeEl : function(){
50111         return this.wrap;
50112     },
50113
50114     getPositionEl : function(){
50115         return this.wrap;
50116     },
50117
50118     // private
50119     onRender : function(ct, position){
50120         
50121         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50122         var style = this.style;
50123         delete this.style;
50124         
50125         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50126         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50127         this.viewEl = this.wrap.createChild({ tag: 'div' });
50128         if (style) {
50129             this.viewEl.applyStyles(style);
50130         }
50131         if (this.width) {
50132             this.viewEl.setWidth(this.width);
50133         }
50134         if (this.height) {
50135             this.viewEl.setHeight(this.height);
50136         }
50137         //if(this.inputValue !== undefined){
50138         //this.setValue(this.value);
50139         
50140         
50141         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50142         
50143         
50144         this.grid.render();
50145         this.grid.getDataSource().on('remove', this.refreshValue, this);
50146         this.grid.getDataSource().on('update', this.refreshValue, this);
50147         this.grid.on('afteredit', this.refreshValue, this);
50148  
50149     },
50150      
50151     
50152     /**
50153      * Sets the value of the item. 
50154      * @param {String} either an object  or a string..
50155      */
50156     setValue : function(v){
50157         //this.value = v;
50158         v = v || []; // empty set..
50159         // this does not seem smart - it really only affects memoryproxy grids..
50160         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50161             var ds = this.grid.getDataSource();
50162             // assumes a json reader..
50163             var data = {}
50164             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50165             ds.loadData( data);
50166         }
50167         // clear selection so it does not get stale.
50168         if (this.grid.sm) { 
50169             this.grid.sm.clearSelections();
50170         }
50171         
50172         Roo.form.GridField.superclass.setValue.call(this, v);
50173         this.refreshValue();
50174         // should load data in the grid really....
50175     },
50176     
50177     // private
50178     refreshValue: function() {
50179          var val = [];
50180         this.grid.getDataSource().each(function(r) {
50181             val.push(r.data);
50182         });
50183         this.el.dom.value = Roo.encode(val);
50184     }
50185     
50186      
50187     
50188     
50189 });/*
50190  * Based on:
50191  * Ext JS Library 1.1.1
50192  * Copyright(c) 2006-2007, Ext JS, LLC.
50193  *
50194  * Originally Released Under LGPL - original licence link has changed is not relivant.
50195  *
50196  * Fork - LGPL
50197  * <script type="text/javascript">
50198  */
50199 /**
50200  * @class Roo.form.DisplayField
50201  * @extends Roo.form.Field
50202  * A generic Field to display non-editable data.
50203  * @cfg {Boolean} closable (true|false) default false
50204  * @constructor
50205  * Creates a new Display Field item.
50206  * @param {Object} config Configuration options
50207  */
50208 Roo.form.DisplayField = function(config){
50209     Roo.form.DisplayField.superclass.constructor.call(this, config);
50210     
50211     this.addEvents({
50212         /**
50213          * @event close
50214          * Fires after the click the close btn
50215              * @param {Roo.form.DisplayField} this
50216              */
50217         close : true
50218     });
50219 };
50220
50221 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50222     inputType:      'hidden',
50223     allowBlank:     true,
50224     readOnly:         true,
50225     
50226  
50227     /**
50228      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50229      */
50230     focusClass : undefined,
50231     /**
50232      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50233      */
50234     fieldClass: 'x-form-field',
50235     
50236      /**
50237      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50238      */
50239     valueRenderer: undefined,
50240     
50241     width: 100,
50242     /**
50243      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50244      * {tag: "input", type: "checkbox", autocomplete: "off"})
50245      */
50246      
50247  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50248  
50249     closable : false,
50250     
50251     onResize : function(){
50252         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50253         
50254     },
50255
50256     initEvents : function(){
50257         // Roo.form.Checkbox.superclass.initEvents.call(this);
50258         // has no events...
50259         
50260         if(this.closable){
50261             this.closeEl.on('click', this.onClose, this);
50262         }
50263        
50264     },
50265
50266
50267     getResizeEl : function(){
50268         return this.wrap;
50269     },
50270
50271     getPositionEl : function(){
50272         return this.wrap;
50273     },
50274
50275     // private
50276     onRender : function(ct, position){
50277         
50278         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50279         //if(this.inputValue !== undefined){
50280         this.wrap = this.el.wrap();
50281         
50282         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50283         
50284         if(this.closable){
50285             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50286         }
50287         
50288         if (this.bodyStyle) {
50289             this.viewEl.applyStyles(this.bodyStyle);
50290         }
50291         //this.viewEl.setStyle('padding', '2px');
50292         
50293         this.setValue(this.value);
50294         
50295     },
50296 /*
50297     // private
50298     initValue : Roo.emptyFn,
50299
50300   */
50301
50302         // private
50303     onClick : function(){
50304         
50305     },
50306
50307     /**
50308      * Sets the checked state of the checkbox.
50309      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50310      */
50311     setValue : function(v){
50312         this.value = v;
50313         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50314         // this might be called before we have a dom element..
50315         if (!this.viewEl) {
50316             return;
50317         }
50318         this.viewEl.dom.innerHTML = html;
50319         Roo.form.DisplayField.superclass.setValue.call(this, v);
50320
50321     },
50322     
50323     onClose : function(e)
50324     {
50325         e.preventDefault();
50326         
50327         this.fireEvent('close', this);
50328     }
50329 });/*
50330  * 
50331  * Licence- LGPL
50332  * 
50333  */
50334
50335 /**
50336  * @class Roo.form.DayPicker
50337  * @extends Roo.form.Field
50338  * A Day picker show [M] [T] [W] ....
50339  * @constructor
50340  * Creates a new Day Picker
50341  * @param {Object} config Configuration options
50342  */
50343 Roo.form.DayPicker= function(config){
50344     Roo.form.DayPicker.superclass.constructor.call(this, config);
50345      
50346 };
50347
50348 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50349     /**
50350      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50351      */
50352     focusClass : undefined,
50353     /**
50354      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50355      */
50356     fieldClass: "x-form-field",
50357    
50358     /**
50359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50360      * {tag: "input", type: "checkbox", autocomplete: "off"})
50361      */
50362     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50363     
50364    
50365     actionMode : 'viewEl', 
50366     //
50367     // private
50368  
50369     inputType : 'hidden',
50370     
50371      
50372     inputElement: false, // real input element?
50373     basedOn: false, // ????
50374     
50375     isFormField: true, // not sure where this is needed!!!!
50376
50377     onResize : function(){
50378         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50379         if(!this.boxLabel){
50380             this.el.alignTo(this.wrap, 'c-c');
50381         }
50382     },
50383
50384     initEvents : function(){
50385         Roo.form.Checkbox.superclass.initEvents.call(this);
50386         this.el.on("click", this.onClick,  this);
50387         this.el.on("change", this.onClick,  this);
50388     },
50389
50390
50391     getResizeEl : function(){
50392         return this.wrap;
50393     },
50394
50395     getPositionEl : function(){
50396         return this.wrap;
50397     },
50398
50399     
50400     // private
50401     onRender : function(ct, position){
50402         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50403        
50404         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50405         
50406         var r1 = '<table><tr>';
50407         var r2 = '<tr class="x-form-daypick-icons">';
50408         for (var i=0; i < 7; i++) {
50409             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50410             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50411         }
50412         
50413         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50414         viewEl.select('img').on('click', this.onClick, this);
50415         this.viewEl = viewEl;   
50416         
50417         
50418         // this will not work on Chrome!!!
50419         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50420         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50421         
50422         
50423           
50424
50425     },
50426
50427     // private
50428     initValue : Roo.emptyFn,
50429
50430     /**
50431      * Returns the checked state of the checkbox.
50432      * @return {Boolean} True if checked, else false
50433      */
50434     getValue : function(){
50435         return this.el.dom.value;
50436         
50437     },
50438
50439         // private
50440     onClick : function(e){ 
50441         //this.setChecked(!this.checked);
50442         Roo.get(e.target).toggleClass('x-menu-item-checked');
50443         this.refreshValue();
50444         //if(this.el.dom.checked != this.checked){
50445         //    this.setValue(this.el.dom.checked);
50446        // }
50447     },
50448     
50449     // private
50450     refreshValue : function()
50451     {
50452         var val = '';
50453         this.viewEl.select('img',true).each(function(e,i,n)  {
50454             val += e.is(".x-menu-item-checked") ? String(n) : '';
50455         });
50456         this.setValue(val, true);
50457     },
50458
50459     /**
50460      * Sets the checked state of the checkbox.
50461      * On is always based on a string comparison between inputValue and the param.
50462      * @param {Boolean/String} value - the value to set 
50463      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50464      */
50465     setValue : function(v,suppressEvent){
50466         if (!this.el.dom) {
50467             return;
50468         }
50469         var old = this.el.dom.value ;
50470         this.el.dom.value = v;
50471         if (suppressEvent) {
50472             return ;
50473         }
50474          
50475         // update display..
50476         this.viewEl.select('img',true).each(function(e,i,n)  {
50477             
50478             var on = e.is(".x-menu-item-checked");
50479             var newv = v.indexOf(String(n)) > -1;
50480             if (on != newv) {
50481                 e.toggleClass('x-menu-item-checked');
50482             }
50483             
50484         });
50485         
50486         
50487         this.fireEvent('change', this, v, old);
50488         
50489         
50490     },
50491    
50492     // handle setting of hidden value by some other method!!?!?
50493     setFromHidden: function()
50494     {
50495         if(!this.el){
50496             return;
50497         }
50498         //console.log("SET FROM HIDDEN");
50499         //alert('setFrom hidden');
50500         this.setValue(this.el.dom.value);
50501     },
50502     
50503     onDestroy : function()
50504     {
50505         if(this.viewEl){
50506             Roo.get(this.viewEl).remove();
50507         }
50508          
50509         Roo.form.DayPicker.superclass.onDestroy.call(this);
50510     }
50511
50512 });/*
50513  * RooJS Library 1.1.1
50514  * Copyright(c) 2008-2011  Alan Knowles
50515  *
50516  * License - LGPL
50517  */
50518  
50519
50520 /**
50521  * @class Roo.form.ComboCheck
50522  * @extends Roo.form.ComboBox
50523  * A combobox for multiple select items.
50524  *
50525  * FIXME - could do with a reset button..
50526  * 
50527  * @constructor
50528  * Create a new ComboCheck
50529  * @param {Object} config Configuration options
50530  */
50531 Roo.form.ComboCheck = function(config){
50532     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50533     // should verify some data...
50534     // like
50535     // hiddenName = required..
50536     // displayField = required
50537     // valudField == required
50538     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50539     var _t = this;
50540     Roo.each(req, function(e) {
50541         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50542             throw "Roo.form.ComboCheck : missing value for: " + e;
50543         }
50544     });
50545     
50546     
50547 };
50548
50549 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50550      
50551      
50552     editable : false,
50553      
50554     selectedClass: 'x-menu-item-checked', 
50555     
50556     // private
50557     onRender : function(ct, position){
50558         var _t = this;
50559         
50560         
50561         
50562         if(!this.tpl){
50563             var cls = 'x-combo-list';
50564
50565             
50566             this.tpl =  new Roo.Template({
50567                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50568                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50569                    '<span>{' + this.displayField + '}</span>' +
50570                     '</div>' 
50571                 
50572             });
50573         }
50574  
50575         
50576         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50577         this.view.singleSelect = false;
50578         this.view.multiSelect = true;
50579         this.view.toggleSelect = true;
50580         this.pageTb.add(new Roo.Toolbar.Fill(), {
50581             
50582             text: 'Done',
50583             handler: function()
50584             {
50585                 _t.collapse();
50586             }
50587         });
50588     },
50589     
50590     onViewOver : function(e, t){
50591         // do nothing...
50592         return;
50593         
50594     },
50595     
50596     onViewClick : function(doFocus,index){
50597         return;
50598         
50599     },
50600     select: function () {
50601         //Roo.log("SELECT CALLED");
50602     },
50603      
50604     selectByValue : function(xv, scrollIntoView){
50605         var ar = this.getValueArray();
50606         var sels = [];
50607         
50608         Roo.each(ar, function(v) {
50609             if(v === undefined || v === null){
50610                 return;
50611             }
50612             var r = this.findRecord(this.valueField, v);
50613             if(r){
50614                 sels.push(this.store.indexOf(r))
50615                 
50616             }
50617         },this);
50618         this.view.select(sels);
50619         return false;
50620     },
50621     
50622     
50623     
50624     onSelect : function(record, index){
50625        // Roo.log("onselect Called");
50626        // this is only called by the clear button now..
50627         this.view.clearSelections();
50628         this.setValue('[]');
50629         if (this.value != this.valueBefore) {
50630             this.fireEvent('change', this, this.value, this.valueBefore);
50631             this.valueBefore = this.value;
50632         }
50633     },
50634     getValueArray : function()
50635     {
50636         var ar = [] ;
50637         
50638         try {
50639             //Roo.log(this.value);
50640             if (typeof(this.value) == 'undefined') {
50641                 return [];
50642             }
50643             var ar = Roo.decode(this.value);
50644             return  ar instanceof Array ? ar : []; //?? valid?
50645             
50646         } catch(e) {
50647             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50648             return [];
50649         }
50650          
50651     },
50652     expand : function ()
50653     {
50654         
50655         Roo.form.ComboCheck.superclass.expand.call(this);
50656         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50657         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50658         
50659
50660     },
50661     
50662     collapse : function(){
50663         Roo.form.ComboCheck.superclass.collapse.call(this);
50664         var sl = this.view.getSelectedIndexes();
50665         var st = this.store;
50666         var nv = [];
50667         var tv = [];
50668         var r;
50669         Roo.each(sl, function(i) {
50670             r = st.getAt(i);
50671             nv.push(r.get(this.valueField));
50672         },this);
50673         this.setValue(Roo.encode(nv));
50674         if (this.value != this.valueBefore) {
50675
50676             this.fireEvent('change', this, this.value, this.valueBefore);
50677             this.valueBefore = this.value;
50678         }
50679         
50680     },
50681     
50682     setValue : function(v){
50683         // Roo.log(v);
50684         this.value = v;
50685         
50686         var vals = this.getValueArray();
50687         var tv = [];
50688         Roo.each(vals, function(k) {
50689             var r = this.findRecord(this.valueField, k);
50690             if(r){
50691                 tv.push(r.data[this.displayField]);
50692             }else if(this.valueNotFoundText !== undefined){
50693                 tv.push( this.valueNotFoundText );
50694             }
50695         },this);
50696        // Roo.log(tv);
50697         
50698         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50699         this.hiddenField.value = v;
50700         this.value = v;
50701     }
50702     
50703 });/*
50704  * Based on:
50705  * Ext JS Library 1.1.1
50706  * Copyright(c) 2006-2007, Ext JS, LLC.
50707  *
50708  * Originally Released Under LGPL - original licence link has changed is not relivant.
50709  *
50710  * Fork - LGPL
50711  * <script type="text/javascript">
50712  */
50713  
50714 /**
50715  * @class Roo.form.Signature
50716  * @extends Roo.form.Field
50717  * Signature field.  
50718  * @constructor
50719  * 
50720  * @param {Object} config Configuration options
50721  */
50722
50723 Roo.form.Signature = function(config){
50724     Roo.form.Signature.superclass.constructor.call(this, config);
50725     
50726     this.addEvents({// not in used??
50727          /**
50728          * @event confirm
50729          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50730              * @param {Roo.form.Signature} combo This combo box
50731              */
50732         'confirm' : true,
50733         /**
50734          * @event reset
50735          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50736              * @param {Roo.form.ComboBox} combo This combo box
50737              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50738              */
50739         'reset' : true
50740     });
50741 };
50742
50743 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50744     /**
50745      * @cfg {Object} labels Label to use when rendering a form.
50746      * defaults to 
50747      * labels : { 
50748      *      clear : "Clear",
50749      *      confirm : "Confirm"
50750      *  }
50751      */
50752     labels : { 
50753         clear : "Clear",
50754         confirm : "Confirm"
50755     },
50756     /**
50757      * @cfg {Number} width The signature panel width (defaults to 300)
50758      */
50759     width: 300,
50760     /**
50761      * @cfg {Number} height The signature panel height (defaults to 100)
50762      */
50763     height : 100,
50764     /**
50765      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50766      */
50767     allowBlank : false,
50768     
50769     //private
50770     // {Object} signPanel The signature SVG panel element (defaults to {})
50771     signPanel : {},
50772     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50773     isMouseDown : false,
50774     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50775     isConfirmed : false,
50776     // {String} signatureTmp SVG mapping string (defaults to empty string)
50777     signatureTmp : '',
50778     
50779     
50780     defaultAutoCreate : { // modified by initCompnoent..
50781         tag: "input",
50782         type:"hidden"
50783     },
50784
50785     // private
50786     onRender : function(ct, position){
50787         
50788         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50789         
50790         this.wrap = this.el.wrap({
50791             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50792         });
50793         
50794         this.createToolbar(this);
50795         this.signPanel = this.wrap.createChild({
50796                 tag: 'div',
50797                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50798             }, this.el
50799         );
50800             
50801         this.svgID = Roo.id();
50802         this.svgEl = this.signPanel.createChild({
50803               xmlns : 'http://www.w3.org/2000/svg',
50804               tag : 'svg',
50805               id : this.svgID + "-svg",
50806               width: this.width,
50807               height: this.height,
50808               viewBox: '0 0 '+this.width+' '+this.height,
50809               cn : [
50810                 {
50811                     tag: "rect",
50812                     id: this.svgID + "-svg-r",
50813                     width: this.width,
50814                     height: this.height,
50815                     fill: "#ffa"
50816                 },
50817                 {
50818                     tag: "line",
50819                     id: this.svgID + "-svg-l",
50820                     x1: "0", // start
50821                     y1: (this.height*0.8), // start set the line in 80% of height
50822                     x2: this.width, // end
50823                     y2: (this.height*0.8), // end set the line in 80% of height
50824                     'stroke': "#666",
50825                     'stroke-width': "1",
50826                     'stroke-dasharray': "3",
50827                     'shape-rendering': "crispEdges",
50828                     'pointer-events': "none"
50829                 },
50830                 {
50831                     tag: "path",
50832                     id: this.svgID + "-svg-p",
50833                     'stroke': "navy",
50834                     'stroke-width': "3",
50835                     'fill': "none",
50836                     'pointer-events': 'none'
50837                 }
50838               ]
50839         });
50840         this.createSVG();
50841         this.svgBox = this.svgEl.dom.getScreenCTM();
50842     },
50843     createSVG : function(){ 
50844         var svg = this.signPanel;
50845         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50846         var t = this;
50847
50848         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50849         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50850         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50851         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50852         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50853         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50854         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50855         
50856     },
50857     isTouchEvent : function(e){
50858         return e.type.match(/^touch/);
50859     },
50860     getCoords : function (e) {
50861         var pt    = this.svgEl.dom.createSVGPoint();
50862         pt.x = e.clientX; 
50863         pt.y = e.clientY;
50864         if (this.isTouchEvent(e)) {
50865             pt.x =  e.targetTouches[0].clientX;
50866             pt.y = e.targetTouches[0].clientY;
50867         }
50868         var a = this.svgEl.dom.getScreenCTM();
50869         var b = a.inverse();
50870         var mx = pt.matrixTransform(b);
50871         return mx.x + ',' + mx.y;
50872     },
50873     //mouse event headler 
50874     down : function (e) {
50875         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50876         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50877         
50878         this.isMouseDown = true;
50879         
50880         e.preventDefault();
50881     },
50882     move : function (e) {
50883         if (this.isMouseDown) {
50884             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50885             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50886         }
50887         
50888         e.preventDefault();
50889     },
50890     up : function (e) {
50891         this.isMouseDown = false;
50892         var sp = this.signatureTmp.split(' ');
50893         
50894         if(sp.length > 1){
50895             if(!sp[sp.length-2].match(/^L/)){
50896                 sp.pop();
50897                 sp.pop();
50898                 sp.push("");
50899                 this.signatureTmp = sp.join(" ");
50900             }
50901         }
50902         if(this.getValue() != this.signatureTmp){
50903             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50904             this.isConfirmed = false;
50905         }
50906         e.preventDefault();
50907     },
50908     
50909     /**
50910      * Protected method that will not generally be called directly. It
50911      * is called when the editor creates its toolbar. Override this method if you need to
50912      * add custom toolbar buttons.
50913      * @param {HtmlEditor} editor
50914      */
50915     createToolbar : function(editor){
50916          function btn(id, toggle, handler){
50917             var xid = fid + '-'+ id ;
50918             return {
50919                 id : xid,
50920                 cmd : id,
50921                 cls : 'x-btn-icon x-edit-'+id,
50922                 enableToggle:toggle !== false,
50923                 scope: editor, // was editor...
50924                 handler:handler||editor.relayBtnCmd,
50925                 clickEvent:'mousedown',
50926                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50927                 tabIndex:-1
50928             };
50929         }
50930         
50931         
50932         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50933         this.tb = tb;
50934         this.tb.add(
50935            {
50936                 cls : ' x-signature-btn x-signature-'+id,
50937                 scope: editor, // was editor...
50938                 handler: this.reset,
50939                 clickEvent:'mousedown',
50940                 text: this.labels.clear
50941             },
50942             {
50943                  xtype : 'Fill',
50944                  xns: Roo.Toolbar
50945             }, 
50946             {
50947                 cls : '  x-signature-btn x-signature-'+id,
50948                 scope: editor, // was editor...
50949                 handler: this.confirmHandler,
50950                 clickEvent:'mousedown',
50951                 text: this.labels.confirm
50952             }
50953         );
50954     
50955     },
50956     //public
50957     /**
50958      * when user is clicked confirm then show this image.....
50959      * 
50960      * @return {String} Image Data URI
50961      */
50962     getImageDataURI : function(){
50963         var svg = this.svgEl.dom.parentNode.innerHTML;
50964         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50965         return src; 
50966     },
50967     /**
50968      * 
50969      * @return {Boolean} this.isConfirmed
50970      */
50971     getConfirmed : function(){
50972         return this.isConfirmed;
50973     },
50974     /**
50975      * 
50976      * @return {Number} this.width
50977      */
50978     getWidth : function(){
50979         return this.width;
50980     },
50981     /**
50982      * 
50983      * @return {Number} this.height
50984      */
50985     getHeight : function(){
50986         return this.height;
50987     },
50988     // private
50989     getSignature : function(){
50990         return this.signatureTmp;
50991     },
50992     // private
50993     reset : function(){
50994         this.signatureTmp = '';
50995         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50996         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50997         this.isConfirmed = false;
50998         Roo.form.Signature.superclass.reset.call(this);
50999     },
51000     setSignature : function(s){
51001         this.signatureTmp = s;
51002         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51003         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51004         this.setValue(s);
51005         this.isConfirmed = false;
51006         Roo.form.Signature.superclass.reset.call(this);
51007     }, 
51008     test : function(){
51009 //        Roo.log(this.signPanel.dom.contentWindow.up())
51010     },
51011     //private
51012     setConfirmed : function(){
51013         
51014         
51015         
51016 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51017     },
51018     // private
51019     confirmHandler : function(){
51020         if(!this.getSignature()){
51021             return;
51022         }
51023         
51024         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51025         this.setValue(this.getSignature());
51026         this.isConfirmed = true;
51027         
51028         this.fireEvent('confirm', this);
51029     },
51030     // private
51031     // Subclasses should provide the validation implementation by overriding this
51032     validateValue : function(value){
51033         if(this.allowBlank){
51034             return true;
51035         }
51036         
51037         if(this.isConfirmed){
51038             return true;
51039         }
51040         return false;
51041     }
51042 });/*
51043  * Based on:
51044  * Ext JS Library 1.1.1
51045  * Copyright(c) 2006-2007, Ext JS, LLC.
51046  *
51047  * Originally Released Under LGPL - original licence link has changed is not relivant.
51048  *
51049  * Fork - LGPL
51050  * <script type="text/javascript">
51051  */
51052  
51053
51054 /**
51055  * @class Roo.form.ComboBox
51056  * @extends Roo.form.TriggerField
51057  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51058  * @constructor
51059  * Create a new ComboBox.
51060  * @param {Object} config Configuration options
51061  */
51062 Roo.form.Select = function(config){
51063     Roo.form.Select.superclass.constructor.call(this, config);
51064      
51065 };
51066
51067 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51068     /**
51069      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51070      */
51071     /**
51072      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51073      * rendering into an Roo.Editor, defaults to false)
51074      */
51075     /**
51076      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51077      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51078      */
51079     /**
51080      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51081      */
51082     /**
51083      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51084      * the dropdown list (defaults to undefined, with no header element)
51085      */
51086
51087      /**
51088      * @cfg {String/Roo.Template} tpl The template to use to render the output
51089      */
51090      
51091     // private
51092     defaultAutoCreate : {tag: "select"  },
51093     /**
51094      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51095      */
51096     listWidth: undefined,
51097     /**
51098      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51099      * mode = 'remote' or 'text' if mode = 'local')
51100      */
51101     displayField: undefined,
51102     /**
51103      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51104      * mode = 'remote' or 'value' if mode = 'local'). 
51105      * Note: use of a valueField requires the user make a selection
51106      * in order for a value to be mapped.
51107      */
51108     valueField: undefined,
51109     
51110     
51111     /**
51112      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51113      * field's data value (defaults to the underlying DOM element's name)
51114      */
51115     hiddenName: undefined,
51116     /**
51117      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51118      */
51119     listClass: '',
51120     /**
51121      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51122      */
51123     selectedClass: 'x-combo-selected',
51124     /**
51125      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51126      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51127      * which displays a downward arrow icon).
51128      */
51129     triggerClass : 'x-form-arrow-trigger',
51130     /**
51131      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51132      */
51133     shadow:'sides',
51134     /**
51135      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51136      * anchor positions (defaults to 'tl-bl')
51137      */
51138     listAlign: 'tl-bl?',
51139     /**
51140      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51141      */
51142     maxHeight: 300,
51143     /**
51144      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51145      * query specified by the allQuery config option (defaults to 'query')
51146      */
51147     triggerAction: 'query',
51148     /**
51149      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51150      * (defaults to 4, does not apply if editable = false)
51151      */
51152     minChars : 4,
51153     /**
51154      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51155      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51156      */
51157     typeAhead: false,
51158     /**
51159      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51160      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51161      */
51162     queryDelay: 500,
51163     /**
51164      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51165      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51166      */
51167     pageSize: 0,
51168     /**
51169      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51170      * when editable = true (defaults to false)
51171      */
51172     selectOnFocus:false,
51173     /**
51174      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51175      */
51176     queryParam: 'query',
51177     /**
51178      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51179      * when mode = 'remote' (defaults to 'Loading...')
51180      */
51181     loadingText: 'Loading...',
51182     /**
51183      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51184      */
51185     resizable: false,
51186     /**
51187      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51188      */
51189     handleHeight : 8,
51190     /**
51191      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51192      * traditional select (defaults to true)
51193      */
51194     editable: true,
51195     /**
51196      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51197      */
51198     allQuery: '',
51199     /**
51200      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51201      */
51202     mode: 'remote',
51203     /**
51204      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51205      * listWidth has a higher value)
51206      */
51207     minListWidth : 70,
51208     /**
51209      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51210      * allow the user to set arbitrary text into the field (defaults to false)
51211      */
51212     forceSelection:false,
51213     /**
51214      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51215      * if typeAhead = true (defaults to 250)
51216      */
51217     typeAheadDelay : 250,
51218     /**
51219      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51220      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51221      */
51222     valueNotFoundText : undefined,
51223     
51224     /**
51225      * @cfg {String} defaultValue The value displayed after loading the store.
51226      */
51227     defaultValue: '',
51228     
51229     /**
51230      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51231      */
51232     blockFocus : false,
51233     
51234     /**
51235      * @cfg {Boolean} disableClear Disable showing of clear button.
51236      */
51237     disableClear : false,
51238     /**
51239      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51240      */
51241     alwaysQuery : false,
51242     
51243     //private
51244     addicon : false,
51245     editicon: false,
51246     
51247     // element that contains real text value.. (when hidden is used..)
51248      
51249     // private
51250     onRender : function(ct, position){
51251         Roo.form.Field.prototype.onRender.call(this, ct, position);
51252         
51253         if(this.store){
51254             this.store.on('beforeload', this.onBeforeLoad, this);
51255             this.store.on('load', this.onLoad, this);
51256             this.store.on('loadexception', this.onLoadException, this);
51257             this.store.load({});
51258         }
51259         
51260         
51261         
51262     },
51263
51264     // private
51265     initEvents : function(){
51266         //Roo.form.ComboBox.superclass.initEvents.call(this);
51267  
51268     },
51269
51270     onDestroy : function(){
51271        
51272         if(this.store){
51273             this.store.un('beforeload', this.onBeforeLoad, this);
51274             this.store.un('load', this.onLoad, this);
51275             this.store.un('loadexception', this.onLoadException, this);
51276         }
51277         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51278     },
51279
51280     // private
51281     fireKey : function(e){
51282         if(e.isNavKeyPress() && !this.list.isVisible()){
51283             this.fireEvent("specialkey", this, e);
51284         }
51285     },
51286
51287     // private
51288     onResize: function(w, h){
51289         
51290         return; 
51291     
51292         
51293     },
51294
51295     /**
51296      * Allow or prevent the user from directly editing the field text.  If false is passed,
51297      * the user will only be able to select from the items defined in the dropdown list.  This method
51298      * is the runtime equivalent of setting the 'editable' config option at config time.
51299      * @param {Boolean} value True to allow the user to directly edit the field text
51300      */
51301     setEditable : function(value){
51302          
51303     },
51304
51305     // private
51306     onBeforeLoad : function(){
51307         
51308         Roo.log("Select before load");
51309         return;
51310     
51311         this.innerList.update(this.loadingText ?
51312                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51313         //this.restrictHeight();
51314         this.selectedIndex = -1;
51315     },
51316
51317     // private
51318     onLoad : function(){
51319
51320     
51321         var dom = this.el.dom;
51322         dom.innerHTML = '';
51323          var od = dom.ownerDocument;
51324          
51325         if (this.emptyText) {
51326             var op = od.createElement('option');
51327             op.setAttribute('value', '');
51328             op.innerHTML = String.format('{0}', this.emptyText);
51329             dom.appendChild(op);
51330         }
51331         if(this.store.getCount() > 0){
51332            
51333             var vf = this.valueField;
51334             var df = this.displayField;
51335             this.store.data.each(function(r) {
51336                 // which colmsn to use... testing - cdoe / title..
51337                 var op = od.createElement('option');
51338                 op.setAttribute('value', r.data[vf]);
51339                 op.innerHTML = String.format('{0}', r.data[df]);
51340                 dom.appendChild(op);
51341             });
51342             if (typeof(this.defaultValue != 'undefined')) {
51343                 this.setValue(this.defaultValue);
51344             }
51345             
51346              
51347         }else{
51348             //this.onEmptyResults();
51349         }
51350         //this.el.focus();
51351     },
51352     // private
51353     onLoadException : function()
51354     {
51355         dom.innerHTML = '';
51356             
51357         Roo.log("Select on load exception");
51358         return;
51359     
51360         this.collapse();
51361         Roo.log(this.store.reader.jsonData);
51362         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51363             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51364         }
51365         
51366         
51367     },
51368     // private
51369     onTypeAhead : function(){
51370          
51371     },
51372
51373     // private
51374     onSelect : function(record, index){
51375         Roo.log('on select?');
51376         return;
51377         if(this.fireEvent('beforeselect', this, record, index) !== false){
51378             this.setFromData(index > -1 ? record.data : false);
51379             this.collapse();
51380             this.fireEvent('select', this, record, index);
51381         }
51382     },
51383
51384     /**
51385      * Returns the currently selected field value or empty string if no value is set.
51386      * @return {String} value The selected value
51387      */
51388     getValue : function(){
51389         var dom = this.el.dom;
51390         this.value = dom.options[dom.selectedIndex].value;
51391         return this.value;
51392         
51393     },
51394
51395     /**
51396      * Clears any text/value currently set in the field
51397      */
51398     clearValue : function(){
51399         this.value = '';
51400         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51401         
51402     },
51403
51404     /**
51405      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51406      * will be displayed in the field.  If the value does not match the data value of an existing item,
51407      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51408      * Otherwise the field will be blank (although the value will still be set).
51409      * @param {String} value The value to match
51410      */
51411     setValue : function(v){
51412         var d = this.el.dom;
51413         for (var i =0; i < d.options.length;i++) {
51414             if (v == d.options[i].value) {
51415                 d.selectedIndex = i;
51416                 this.value = v;
51417                 return;
51418             }
51419         }
51420         this.clearValue();
51421     },
51422     /**
51423      * @property {Object} the last set data for the element
51424      */
51425     
51426     lastData : false,
51427     /**
51428      * Sets the value of the field based on a object which is related to the record format for the store.
51429      * @param {Object} value the value to set as. or false on reset?
51430      */
51431     setFromData : function(o){
51432         Roo.log('setfrom data?');
51433          
51434         
51435         
51436     },
51437     // private
51438     reset : function(){
51439         this.clearValue();
51440     },
51441     // private
51442     findRecord : function(prop, value){
51443         
51444         return false;
51445     
51446         var record;
51447         if(this.store.getCount() > 0){
51448             this.store.each(function(r){
51449                 if(r.data[prop] == value){
51450                     record = r;
51451                     return false;
51452                 }
51453                 return true;
51454             });
51455         }
51456         return record;
51457     },
51458     
51459     getName: function()
51460     {
51461         // returns hidden if it's set..
51462         if (!this.rendered) {return ''};
51463         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51464         
51465     },
51466      
51467
51468     
51469
51470     // private
51471     onEmptyResults : function(){
51472         Roo.log('empty results');
51473         //this.collapse();
51474     },
51475
51476     /**
51477      * Returns true if the dropdown list is expanded, else false.
51478      */
51479     isExpanded : function(){
51480         return false;
51481     },
51482
51483     /**
51484      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51485      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51486      * @param {String} value The data value of the item to select
51487      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51488      * selected item if it is not currently in view (defaults to true)
51489      * @return {Boolean} True if the value matched an item in the list, else false
51490      */
51491     selectByValue : function(v, scrollIntoView){
51492         Roo.log('select By Value');
51493         return false;
51494     
51495         if(v !== undefined && v !== null){
51496             var r = this.findRecord(this.valueField || this.displayField, v);
51497             if(r){
51498                 this.select(this.store.indexOf(r), scrollIntoView);
51499                 return true;
51500             }
51501         }
51502         return false;
51503     },
51504
51505     /**
51506      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51507      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51508      * @param {Number} index The zero-based index of the list item to select
51509      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51510      * selected item if it is not currently in view (defaults to true)
51511      */
51512     select : function(index, scrollIntoView){
51513         Roo.log('select ');
51514         return  ;
51515         
51516         this.selectedIndex = index;
51517         this.view.select(index);
51518         if(scrollIntoView !== false){
51519             var el = this.view.getNode(index);
51520             if(el){
51521                 this.innerList.scrollChildIntoView(el, false);
51522             }
51523         }
51524     },
51525
51526       
51527
51528     // private
51529     validateBlur : function(){
51530         
51531         return;
51532         
51533     },
51534
51535     // private
51536     initQuery : function(){
51537         this.doQuery(this.getRawValue());
51538     },
51539
51540     // private
51541     doForce : function(){
51542         if(this.el.dom.value.length > 0){
51543             this.el.dom.value =
51544                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51545              
51546         }
51547     },
51548
51549     /**
51550      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51551      * query allowing the query action to be canceled if needed.
51552      * @param {String} query The SQL query to execute
51553      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51554      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51555      * saved in the current store (defaults to false)
51556      */
51557     doQuery : function(q, forceAll){
51558         
51559         Roo.log('doQuery?');
51560         if(q === undefined || q === null){
51561             q = '';
51562         }
51563         var qe = {
51564             query: q,
51565             forceAll: forceAll,
51566             combo: this,
51567             cancel:false
51568         };
51569         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51570             return false;
51571         }
51572         q = qe.query;
51573         forceAll = qe.forceAll;
51574         if(forceAll === true || (q.length >= this.minChars)){
51575             if(this.lastQuery != q || this.alwaysQuery){
51576                 this.lastQuery = q;
51577                 if(this.mode == 'local'){
51578                     this.selectedIndex = -1;
51579                     if(forceAll){
51580                         this.store.clearFilter();
51581                     }else{
51582                         this.store.filter(this.displayField, q);
51583                     }
51584                     this.onLoad();
51585                 }else{
51586                     this.store.baseParams[this.queryParam] = q;
51587                     this.store.load({
51588                         params: this.getParams(q)
51589                     });
51590                     this.expand();
51591                 }
51592             }else{
51593                 this.selectedIndex = -1;
51594                 this.onLoad();   
51595             }
51596         }
51597     },
51598
51599     // private
51600     getParams : function(q){
51601         var p = {};
51602         //p[this.queryParam] = q;
51603         if(this.pageSize){
51604             p.start = 0;
51605             p.limit = this.pageSize;
51606         }
51607         return p;
51608     },
51609
51610     /**
51611      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51612      */
51613     collapse : function(){
51614         
51615     },
51616
51617     // private
51618     collapseIf : function(e){
51619         
51620     },
51621
51622     /**
51623      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51624      */
51625     expand : function(){
51626         
51627     } ,
51628
51629     // private
51630      
51631
51632     /** 
51633     * @cfg {Boolean} grow 
51634     * @hide 
51635     */
51636     /** 
51637     * @cfg {Number} growMin 
51638     * @hide 
51639     */
51640     /** 
51641     * @cfg {Number} growMax 
51642     * @hide 
51643     */
51644     /**
51645      * @hide
51646      * @method autoSize
51647      */
51648     
51649     setWidth : function()
51650     {
51651         
51652     },
51653     getResizeEl : function(){
51654         return this.el;
51655     }
51656 });//<script type="text/javasscript">
51657  
51658
51659 /**
51660  * @class Roo.DDView
51661  * A DnD enabled version of Roo.View.
51662  * @param {Element/String} container The Element in which to create the View.
51663  * @param {String} tpl The template string used to create the markup for each element of the View
51664  * @param {Object} config The configuration properties. These include all the config options of
51665  * {@link Roo.View} plus some specific to this class.<br>
51666  * <p>
51667  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51668  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51669  * <p>
51670  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51671 .x-view-drag-insert-above {
51672         border-top:1px dotted #3366cc;
51673 }
51674 .x-view-drag-insert-below {
51675         border-bottom:1px dotted #3366cc;
51676 }
51677 </code></pre>
51678  * 
51679  */
51680  
51681 Roo.DDView = function(container, tpl, config) {
51682     Roo.DDView.superclass.constructor.apply(this, arguments);
51683     this.getEl().setStyle("outline", "0px none");
51684     this.getEl().unselectable();
51685     if (this.dragGroup) {
51686         this.setDraggable(this.dragGroup.split(","));
51687     }
51688     if (this.dropGroup) {
51689         this.setDroppable(this.dropGroup.split(","));
51690     }
51691     if (this.deletable) {
51692         this.setDeletable();
51693     }
51694     this.isDirtyFlag = false;
51695         this.addEvents({
51696                 "drop" : true
51697         });
51698 };
51699
51700 Roo.extend(Roo.DDView, Roo.View, {
51701 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51702 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51703 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51704 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51705
51706         isFormField: true,
51707
51708         reset: Roo.emptyFn,
51709         
51710         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51711
51712         validate: function() {
51713                 return true;
51714         },
51715         
51716         destroy: function() {
51717                 this.purgeListeners();
51718                 this.getEl.removeAllListeners();
51719                 this.getEl().remove();
51720                 if (this.dragZone) {
51721                         if (this.dragZone.destroy) {
51722                                 this.dragZone.destroy();
51723                         }
51724                 }
51725                 if (this.dropZone) {
51726                         if (this.dropZone.destroy) {
51727                                 this.dropZone.destroy();
51728                         }
51729                 }
51730         },
51731
51732 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51733         getName: function() {
51734                 return this.name;
51735         },
51736
51737 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51738         setValue: function(v) {
51739                 if (!this.store) {
51740                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51741                 }
51742                 var data = {};
51743                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51744                 this.store.proxy = new Roo.data.MemoryProxy(data);
51745                 this.store.load();
51746         },
51747
51748 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51749         getValue: function() {
51750                 var result = '(';
51751                 this.store.each(function(rec) {
51752                         result += rec.id + ',';
51753                 });
51754                 return result.substr(0, result.length - 1) + ')';
51755         },
51756         
51757         getIds: function() {
51758                 var i = 0, result = new Array(this.store.getCount());
51759                 this.store.each(function(rec) {
51760                         result[i++] = rec.id;
51761                 });
51762                 return result;
51763         },
51764         
51765         isDirty: function() {
51766                 return this.isDirtyFlag;
51767         },
51768
51769 /**
51770  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51771  *      whole Element becomes the target, and this causes the drop gesture to append.
51772  */
51773     getTargetFromEvent : function(e) {
51774                 var target = e.getTarget();
51775                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51776                 target = target.parentNode;
51777                 }
51778                 if (!target) {
51779                         target = this.el.dom.lastChild || this.el.dom;
51780                 }
51781                 return target;
51782     },
51783
51784 /**
51785  *      Create the drag data which consists of an object which has the property "ddel" as
51786  *      the drag proxy element. 
51787  */
51788     getDragData : function(e) {
51789         var target = this.findItemFromChild(e.getTarget());
51790                 if(target) {
51791                         this.handleSelection(e);
51792                         var selNodes = this.getSelectedNodes();
51793             var dragData = {
51794                 source: this,
51795                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51796                 nodes: selNodes,
51797                 records: []
51798                         };
51799                         var selectedIndices = this.getSelectedIndexes();
51800                         for (var i = 0; i < selectedIndices.length; i++) {
51801                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51802                         }
51803                         if (selNodes.length == 1) {
51804                                 dragData.ddel = target.cloneNode(true); // the div element
51805                         } else {
51806                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51807                                 div.className = 'multi-proxy';
51808                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51809                                         div.appendChild(selNodes[i].cloneNode(true));
51810                                 }
51811                                 dragData.ddel = div;
51812                         }
51813             //console.log(dragData)
51814             //console.log(dragData.ddel.innerHTML)
51815                         return dragData;
51816                 }
51817         //console.log('nodragData')
51818                 return false;
51819     },
51820     
51821 /**     Specify to which ddGroup items in this DDView may be dragged. */
51822     setDraggable: function(ddGroup) {
51823         if (ddGroup instanceof Array) {
51824                 Roo.each(ddGroup, this.setDraggable, this);
51825                 return;
51826         }
51827         if (this.dragZone) {
51828                 this.dragZone.addToGroup(ddGroup);
51829         } else {
51830                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51831                                 containerScroll: true,
51832                                 ddGroup: ddGroup 
51833
51834                         });
51835 //                      Draggability implies selection. DragZone's mousedown selects the element.
51836                         if (!this.multiSelect) { this.singleSelect = true; }
51837
51838 //                      Wire the DragZone's handlers up to methods in *this*
51839                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51840                 }
51841     },
51842
51843 /**     Specify from which ddGroup this DDView accepts drops. */
51844     setDroppable: function(ddGroup) {
51845         if (ddGroup instanceof Array) {
51846                 Roo.each(ddGroup, this.setDroppable, this);
51847                 return;
51848         }
51849         if (this.dropZone) {
51850                 this.dropZone.addToGroup(ddGroup);
51851         } else {
51852                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51853                                 containerScroll: true,
51854                                 ddGroup: ddGroup
51855                         });
51856
51857 //                      Wire the DropZone's handlers up to methods in *this*
51858                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51859                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51860                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51861                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51862                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51863                 }
51864     },
51865
51866 /**     Decide whether to drop above or below a View node. */
51867     getDropPoint : function(e, n, dd){
51868         if (n == this.el.dom) { return "above"; }
51869                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51870                 var c = t + (b - t) / 2;
51871                 var y = Roo.lib.Event.getPageY(e);
51872                 if(y <= c) {
51873                         return "above";
51874                 }else{
51875                         return "below";
51876                 }
51877     },
51878
51879     onNodeEnter : function(n, dd, e, data){
51880                 return false;
51881     },
51882     
51883     onNodeOver : function(n, dd, e, data){
51884                 var pt = this.getDropPoint(e, n, dd);
51885                 // set the insert point style on the target node
51886                 var dragElClass = this.dropNotAllowed;
51887                 if (pt) {
51888                         var targetElClass;
51889                         if (pt == "above"){
51890                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51891                                 targetElClass = "x-view-drag-insert-above";
51892                         } else {
51893                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51894                                 targetElClass = "x-view-drag-insert-below";
51895                         }
51896                         if (this.lastInsertClass != targetElClass){
51897                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51898                                 this.lastInsertClass = targetElClass;
51899                         }
51900                 }
51901                 return dragElClass;
51902         },
51903
51904     onNodeOut : function(n, dd, e, data){
51905                 this.removeDropIndicators(n);
51906     },
51907
51908     onNodeDrop : function(n, dd, e, data){
51909         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51910                 return false;
51911         }
51912         var pt = this.getDropPoint(e, n, dd);
51913                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51914                 if (pt == "below") { insertAt++; }
51915                 for (var i = 0; i < data.records.length; i++) {
51916                         var r = data.records[i];
51917                         var dup = this.store.getById(r.id);
51918                         if (dup && (dd != this.dragZone)) {
51919                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51920                         } else {
51921                                 if (data.copy) {
51922                                         this.store.insert(insertAt++, r.copy());
51923                                 } else {
51924                                         data.source.isDirtyFlag = true;
51925                                         r.store.remove(r);
51926                                         this.store.insert(insertAt++, r);
51927                                 }
51928                                 this.isDirtyFlag = true;
51929                         }
51930                 }
51931                 this.dragZone.cachedTarget = null;
51932                 return true;
51933     },
51934
51935     removeDropIndicators : function(n){
51936                 if(n){
51937                         Roo.fly(n).removeClass([
51938                                 "x-view-drag-insert-above",
51939                                 "x-view-drag-insert-below"]);
51940                         this.lastInsertClass = "_noclass";
51941                 }
51942     },
51943
51944 /**
51945  *      Utility method. Add a delete option to the DDView's context menu.
51946  *      @param {String} imageUrl The URL of the "delete" icon image.
51947  */
51948         setDeletable: function(imageUrl) {
51949                 if (!this.singleSelect && !this.multiSelect) {
51950                         this.singleSelect = true;
51951                 }
51952                 var c = this.getContextMenu();
51953                 this.contextMenu.on("itemclick", function(item) {
51954                         switch (item.id) {
51955                                 case "delete":
51956                                         this.remove(this.getSelectedIndexes());
51957                                         break;
51958                         }
51959                 }, this);
51960                 this.contextMenu.add({
51961                         icon: imageUrl,
51962                         id: "delete",
51963                         text: 'Delete'
51964                 });
51965         },
51966         
51967 /**     Return the context menu for this DDView. */
51968         getContextMenu: function() {
51969                 if (!this.contextMenu) {
51970 //                      Create the View's context menu
51971                         this.contextMenu = new Roo.menu.Menu({
51972                                 id: this.id + "-contextmenu"
51973                         });
51974                         this.el.on("contextmenu", this.showContextMenu, this);
51975                 }
51976                 return this.contextMenu;
51977         },
51978         
51979         disableContextMenu: function() {
51980                 if (this.contextMenu) {
51981                         this.el.un("contextmenu", this.showContextMenu, this);
51982                 }
51983         },
51984
51985         showContextMenu: function(e, item) {
51986         item = this.findItemFromChild(e.getTarget());
51987                 if (item) {
51988                         e.stopEvent();
51989                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51990                         this.contextMenu.showAt(e.getXY());
51991             }
51992     },
51993
51994 /**
51995  *      Remove {@link Roo.data.Record}s at the specified indices.
51996  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51997  */
51998     remove: function(selectedIndices) {
51999                 selectedIndices = [].concat(selectedIndices);
52000                 for (var i = 0; i < selectedIndices.length; i++) {
52001                         var rec = this.store.getAt(selectedIndices[i]);
52002                         this.store.remove(rec);
52003                 }
52004     },
52005
52006 /**
52007  *      Double click fires the event, but also, if this is draggable, and there is only one other
52008  *      related DropZone, it transfers the selected node.
52009  */
52010     onDblClick : function(e){
52011         var item = this.findItemFromChild(e.getTarget());
52012         if(item){
52013             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52014                 return false;
52015             }
52016             if (this.dragGroup) {
52017                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52018                     while (targets.indexOf(this.dropZone) > -1) {
52019                             targets.remove(this.dropZone);
52020                                 }
52021                     if (targets.length == 1) {
52022                                         this.dragZone.cachedTarget = null;
52023                         var el = Roo.get(targets[0].getEl());
52024                         var box = el.getBox(true);
52025                         targets[0].onNodeDrop(el.dom, {
52026                                 target: el.dom,
52027                                 xy: [box.x, box.y + box.height - 1]
52028                         }, null, this.getDragData(e));
52029                     }
52030                 }
52031         }
52032     },
52033     
52034     handleSelection: function(e) {
52035                 this.dragZone.cachedTarget = null;
52036         var item = this.findItemFromChild(e.getTarget());
52037         if (!item) {
52038                 this.clearSelections(true);
52039                 return;
52040         }
52041                 if (item && (this.multiSelect || this.singleSelect)){
52042                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52043                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52044                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52045                                 this.unselect(item);
52046                         } else {
52047                                 this.select(item, this.multiSelect && e.ctrlKey);
52048                                 this.lastSelection = item;
52049                         }
52050                 }
52051     },
52052
52053     onItemClick : function(item, index, e){
52054                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52055                         return false;
52056                 }
52057                 return true;
52058     },
52059
52060     unselect : function(nodeInfo, suppressEvent){
52061                 var node = this.getNode(nodeInfo);
52062                 if(node && this.isSelected(node)){
52063                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52064                                 Roo.fly(node).removeClass(this.selectedClass);
52065                                 this.selections.remove(node);
52066                                 if(!suppressEvent){
52067                                         this.fireEvent("selectionchange", this, this.selections);
52068                                 }
52069                         }
52070                 }
52071     }
52072 });
52073 /*
52074  * Based on:
52075  * Ext JS Library 1.1.1
52076  * Copyright(c) 2006-2007, Ext JS, LLC.
52077  *
52078  * Originally Released Under LGPL - original licence link has changed is not relivant.
52079  *
52080  * Fork - LGPL
52081  * <script type="text/javascript">
52082  */
52083  
52084 /**
52085  * @class Roo.LayoutManager
52086  * @extends Roo.util.Observable
52087  * Base class for layout managers.
52088  */
52089 Roo.LayoutManager = function(container, config){
52090     Roo.LayoutManager.superclass.constructor.call(this);
52091     this.el = Roo.get(container);
52092     // ie scrollbar fix
52093     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52094         document.body.scroll = "no";
52095     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52096         this.el.position('relative');
52097     }
52098     this.id = this.el.id;
52099     this.el.addClass("x-layout-container");
52100     /** false to disable window resize monitoring @type Boolean */
52101     this.monitorWindowResize = true;
52102     this.regions = {};
52103     this.addEvents({
52104         /**
52105          * @event layout
52106          * Fires when a layout is performed. 
52107          * @param {Roo.LayoutManager} this
52108          */
52109         "layout" : true,
52110         /**
52111          * @event regionresized
52112          * Fires when the user resizes a region. 
52113          * @param {Roo.LayoutRegion} region The resized region
52114          * @param {Number} newSize The new size (width for east/west, height for north/south)
52115          */
52116         "regionresized" : true,
52117         /**
52118          * @event regioncollapsed
52119          * Fires when a region is collapsed. 
52120          * @param {Roo.LayoutRegion} region The collapsed region
52121          */
52122         "regioncollapsed" : true,
52123         /**
52124          * @event regionexpanded
52125          * Fires when a region is expanded.  
52126          * @param {Roo.LayoutRegion} region The expanded region
52127          */
52128         "regionexpanded" : true
52129     });
52130     this.updating = false;
52131     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52132 };
52133
52134 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52135     /**
52136      * Returns true if this layout is currently being updated
52137      * @return {Boolean}
52138      */
52139     isUpdating : function(){
52140         return this.updating; 
52141     },
52142     
52143     /**
52144      * Suspend the LayoutManager from doing auto-layouts while
52145      * making multiple add or remove calls
52146      */
52147     beginUpdate : function(){
52148         this.updating = true;    
52149     },
52150     
52151     /**
52152      * Restore auto-layouts and optionally disable the manager from performing a layout
52153      * @param {Boolean} noLayout true to disable a layout update 
52154      */
52155     endUpdate : function(noLayout){
52156         this.updating = false;
52157         if(!noLayout){
52158             this.layout();
52159         }    
52160     },
52161     
52162     layout: function(){
52163         
52164     },
52165     
52166     onRegionResized : function(region, newSize){
52167         this.fireEvent("regionresized", region, newSize);
52168         this.layout();
52169     },
52170     
52171     onRegionCollapsed : function(region){
52172         this.fireEvent("regioncollapsed", region);
52173     },
52174     
52175     onRegionExpanded : function(region){
52176         this.fireEvent("regionexpanded", region);
52177     },
52178         
52179     /**
52180      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52181      * performs box-model adjustments.
52182      * @return {Object} The size as an object {width: (the width), height: (the height)}
52183      */
52184     getViewSize : function(){
52185         var size;
52186         if(this.el.dom != document.body){
52187             size = this.el.getSize();
52188         }else{
52189             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52190         }
52191         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52192         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52193         return size;
52194     },
52195     
52196     /**
52197      * Returns the Element this layout is bound to.
52198      * @return {Roo.Element}
52199      */
52200     getEl : function(){
52201         return this.el;
52202     },
52203     
52204     /**
52205      * Returns the specified region.
52206      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52207      * @return {Roo.LayoutRegion}
52208      */
52209     getRegion : function(target){
52210         return this.regions[target.toLowerCase()];
52211     },
52212     
52213     onWindowResize : function(){
52214         if(this.monitorWindowResize){
52215             this.layout();
52216         }
52217     }
52218 });/*
52219  * Based on:
52220  * Ext JS Library 1.1.1
52221  * Copyright(c) 2006-2007, Ext JS, LLC.
52222  *
52223  * Originally Released Under LGPL - original licence link has changed is not relivant.
52224  *
52225  * Fork - LGPL
52226  * <script type="text/javascript">
52227  */
52228 /**
52229  * @class Roo.BorderLayout
52230  * @extends Roo.LayoutManager
52231  * @children Roo.ContentPanel
52232  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52233  * please see: <br><br>
52234  * <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>
52235  * <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>
52236  * Example:
52237  <pre><code>
52238  var layout = new Roo.BorderLayout(document.body, {
52239     north: {
52240         initialSize: 25,
52241         titlebar: false
52242     },
52243     west: {
52244         split:true,
52245         initialSize: 200,
52246         minSize: 175,
52247         maxSize: 400,
52248         titlebar: true,
52249         collapsible: true
52250     },
52251     east: {
52252         split:true,
52253         initialSize: 202,
52254         minSize: 175,
52255         maxSize: 400,
52256         titlebar: true,
52257         collapsible: true
52258     },
52259     south: {
52260         split:true,
52261         initialSize: 100,
52262         minSize: 100,
52263         maxSize: 200,
52264         titlebar: true,
52265         collapsible: true
52266     },
52267     center: {
52268         titlebar: true,
52269         autoScroll:true,
52270         resizeTabs: true,
52271         minTabWidth: 50,
52272         preferredTabWidth: 150
52273     }
52274 });
52275
52276 // shorthand
52277 var CP = Roo.ContentPanel;
52278
52279 layout.beginUpdate();
52280 layout.add("north", new CP("north", "North"));
52281 layout.add("south", new CP("south", {title: "South", closable: true}));
52282 layout.add("west", new CP("west", {title: "West"}));
52283 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52284 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52285 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52286 layout.getRegion("center").showPanel("center1");
52287 layout.endUpdate();
52288 </code></pre>
52289
52290 <b>The container the layout is rendered into can be either the body element or any other element.
52291 If it is not the body element, the container needs to either be an absolute positioned element,
52292 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52293 the container size if it is not the body element.</b>
52294
52295 * @constructor
52296 * Create a new BorderLayout
52297 * @param {String/HTMLElement/Element} container The container this layout is bound to
52298 * @param {Object} config Configuration options
52299  */
52300 Roo.BorderLayout = function(container, config){
52301     config = config || {};
52302     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52303     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52304     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52305         var target = this.factory.validRegions[i];
52306         if(config[target]){
52307             this.addRegion(target, config[target]);
52308         }
52309     }
52310 };
52311
52312 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52313         
52314         /**
52315          * @cfg {Roo.LayoutRegion} east
52316          */
52317         /**
52318          * @cfg {Roo.LayoutRegion} west
52319          */
52320         /**
52321          * @cfg {Roo.LayoutRegion} north
52322          */
52323         /**
52324          * @cfg {Roo.LayoutRegion} south
52325          */
52326         /**
52327          * @cfg {Roo.LayoutRegion} center
52328          */
52329     /**
52330      * Creates and adds a new region if it doesn't already exist.
52331      * @param {String} target The target region key (north, south, east, west or center).
52332      * @param {Object} config The regions config object
52333      * @return {BorderLayoutRegion} The new region
52334      */
52335     addRegion : function(target, config){
52336         if(!this.regions[target]){
52337             var r = this.factory.create(target, this, config);
52338             this.bindRegion(target, r);
52339         }
52340         return this.regions[target];
52341     },
52342
52343     // private (kinda)
52344     bindRegion : function(name, r){
52345         this.regions[name] = r;
52346         r.on("visibilitychange", this.layout, this);
52347         r.on("paneladded", this.layout, this);
52348         r.on("panelremoved", this.layout, this);
52349         r.on("invalidated", this.layout, this);
52350         r.on("resized", this.onRegionResized, this);
52351         r.on("collapsed", this.onRegionCollapsed, this);
52352         r.on("expanded", this.onRegionExpanded, this);
52353     },
52354
52355     /**
52356      * Performs a layout update.
52357      */
52358     layout : function(){
52359         if(this.updating) {
52360             return;
52361         }
52362         var size = this.getViewSize();
52363         var w = size.width;
52364         var h = size.height;
52365         var centerW = w;
52366         var centerH = h;
52367         var centerY = 0;
52368         var centerX = 0;
52369         //var x = 0, y = 0;
52370
52371         var rs = this.regions;
52372         var north = rs["north"];
52373         var south = rs["south"]; 
52374         var west = rs["west"];
52375         var east = rs["east"];
52376         var center = rs["center"];
52377         //if(this.hideOnLayout){ // not supported anymore
52378             //c.el.setStyle("display", "none");
52379         //}
52380         if(north && north.isVisible()){
52381             var b = north.getBox();
52382             var m = north.getMargins();
52383             b.width = w - (m.left+m.right);
52384             b.x = m.left;
52385             b.y = m.top;
52386             centerY = b.height + b.y + m.bottom;
52387             centerH -= centerY;
52388             north.updateBox(this.safeBox(b));
52389         }
52390         if(south && south.isVisible()){
52391             var b = south.getBox();
52392             var m = south.getMargins();
52393             b.width = w - (m.left+m.right);
52394             b.x = m.left;
52395             var totalHeight = (b.height + m.top + m.bottom);
52396             b.y = h - totalHeight + m.top;
52397             centerH -= totalHeight;
52398             south.updateBox(this.safeBox(b));
52399         }
52400         if(west && west.isVisible()){
52401             var b = west.getBox();
52402             var m = west.getMargins();
52403             b.height = centerH - (m.top+m.bottom);
52404             b.x = m.left;
52405             b.y = centerY + m.top;
52406             var totalWidth = (b.width + m.left + m.right);
52407             centerX += totalWidth;
52408             centerW -= totalWidth;
52409             west.updateBox(this.safeBox(b));
52410         }
52411         if(east && east.isVisible()){
52412             var b = east.getBox();
52413             var m = east.getMargins();
52414             b.height = centerH - (m.top+m.bottom);
52415             var totalWidth = (b.width + m.left + m.right);
52416             b.x = w - totalWidth + m.left;
52417             b.y = centerY + m.top;
52418             centerW -= totalWidth;
52419             east.updateBox(this.safeBox(b));
52420         }
52421         if(center){
52422             var m = center.getMargins();
52423             var centerBox = {
52424                 x: centerX + m.left,
52425                 y: centerY + m.top,
52426                 width: centerW - (m.left+m.right),
52427                 height: centerH - (m.top+m.bottom)
52428             };
52429             //if(this.hideOnLayout){
52430                 //center.el.setStyle("display", "block");
52431             //}
52432             center.updateBox(this.safeBox(centerBox));
52433         }
52434         this.el.repaint();
52435         this.fireEvent("layout", this);
52436     },
52437
52438     // private
52439     safeBox : function(box){
52440         box.width = Math.max(0, box.width);
52441         box.height = Math.max(0, box.height);
52442         return box;
52443     },
52444
52445     /**
52446      * Adds a ContentPanel (or subclass) to this layout.
52447      * @param {String} target The target region key (north, south, east, west or center).
52448      * @param {Roo.ContentPanel} panel The panel to add
52449      * @return {Roo.ContentPanel} The added panel
52450      */
52451     add : function(target, panel){
52452          
52453         target = target.toLowerCase();
52454         return this.regions[target].add(panel);
52455     },
52456
52457     /**
52458      * Remove a ContentPanel (or subclass) to this layout.
52459      * @param {String} target The target region key (north, south, east, west or center).
52460      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52461      * @return {Roo.ContentPanel} The removed panel
52462      */
52463     remove : function(target, panel){
52464         target = target.toLowerCase();
52465         return this.regions[target].remove(panel);
52466     },
52467
52468     /**
52469      * Searches all regions for a panel with the specified id
52470      * @param {String} panelId
52471      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52472      */
52473     findPanel : function(panelId){
52474         var rs = this.regions;
52475         for(var target in rs){
52476             if(typeof rs[target] != "function"){
52477                 var p = rs[target].getPanel(panelId);
52478                 if(p){
52479                     return p;
52480                 }
52481             }
52482         }
52483         return null;
52484     },
52485
52486     /**
52487      * Searches all regions for a panel with the specified id and activates (shows) it.
52488      * @param {String/ContentPanel} panelId The panels id or the panel itself
52489      * @return {Roo.ContentPanel} The shown panel or null
52490      */
52491     showPanel : function(panelId) {
52492       var rs = this.regions;
52493       for(var target in rs){
52494          var r = rs[target];
52495          if(typeof r != "function"){
52496             if(r.hasPanel(panelId)){
52497                return r.showPanel(panelId);
52498             }
52499          }
52500       }
52501       return null;
52502    },
52503
52504    /**
52505      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52506      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52507      */
52508     restoreState : function(provider){
52509         if(!provider){
52510             provider = Roo.state.Manager;
52511         }
52512         var sm = new Roo.LayoutStateManager();
52513         sm.init(this, provider);
52514     },
52515
52516     /**
52517      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52518      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52519      * a valid ContentPanel config object.  Example:
52520      * <pre><code>
52521 // Create the main layout
52522 var layout = new Roo.BorderLayout('main-ct', {
52523     west: {
52524         split:true,
52525         minSize: 175,
52526         titlebar: true
52527     },
52528     center: {
52529         title:'Components'
52530     }
52531 }, 'main-ct');
52532
52533 // Create and add multiple ContentPanels at once via configs
52534 layout.batchAdd({
52535    west: {
52536        id: 'source-files',
52537        autoCreate:true,
52538        title:'Ext Source Files',
52539        autoScroll:true,
52540        fitToFrame:true
52541    },
52542    center : {
52543        el: cview,
52544        autoScroll:true,
52545        fitToFrame:true,
52546        toolbar: tb,
52547        resizeEl:'cbody'
52548    }
52549 });
52550 </code></pre>
52551      * @param {Object} regions An object containing ContentPanel configs by region name
52552      */
52553     batchAdd : function(regions){
52554         this.beginUpdate();
52555         for(var rname in regions){
52556             var lr = this.regions[rname];
52557             if(lr){
52558                 this.addTypedPanels(lr, regions[rname]);
52559             }
52560         }
52561         this.endUpdate();
52562     },
52563
52564     // private
52565     addTypedPanels : function(lr, ps){
52566         if(typeof ps == 'string'){
52567             lr.add(new Roo.ContentPanel(ps));
52568         }
52569         else if(ps instanceof Array){
52570             for(var i =0, len = ps.length; i < len; i++){
52571                 this.addTypedPanels(lr, ps[i]);
52572             }
52573         }
52574         else if(!ps.events){ // raw config?
52575             var el = ps.el;
52576             delete ps.el; // prevent conflict
52577             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52578         }
52579         else {  // panel object assumed!
52580             lr.add(ps);
52581         }
52582     },
52583     /**
52584      * Adds a xtype elements to the layout.
52585      * <pre><code>
52586
52587 layout.addxtype({
52588        xtype : 'ContentPanel',
52589        region: 'west',
52590        items: [ .... ]
52591    }
52592 );
52593
52594 layout.addxtype({
52595         xtype : 'NestedLayoutPanel',
52596         region: 'west',
52597         layout: {
52598            center: { },
52599            west: { }   
52600         },
52601         items : [ ... list of content panels or nested layout panels.. ]
52602    }
52603 );
52604 </code></pre>
52605      * @param {Object} cfg Xtype definition of item to add.
52606      */
52607     addxtype : function(cfg)
52608     {
52609         // basically accepts a pannel...
52610         // can accept a layout region..!?!?
52611         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52612         
52613         if (!cfg.xtype.match(/Panel$/)) {
52614             return false;
52615         }
52616         var ret = false;
52617         
52618         if (typeof(cfg.region) == 'undefined') {
52619             Roo.log("Failed to add Panel, region was not set");
52620             Roo.log(cfg);
52621             return false;
52622         }
52623         var region = cfg.region;
52624         delete cfg.region;
52625         
52626           
52627         var xitems = [];
52628         if (cfg.items) {
52629             xitems = cfg.items;
52630             delete cfg.items;
52631         }
52632         var nb = false;
52633         
52634         switch(cfg.xtype) 
52635         {
52636             case 'ContentPanel':  // ContentPanel (el, cfg)
52637             case 'ScrollPanel':  // ContentPanel (el, cfg)
52638             case 'ViewPanel': 
52639                 if(cfg.autoCreate) {
52640                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52641                 } else {
52642                     var el = this.el.createChild();
52643                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52644                 }
52645                 
52646                 this.add(region, ret);
52647                 break;
52648             
52649             
52650             case 'TreePanel': // our new panel!
52651                 cfg.el = this.el.createChild();
52652                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52653                 this.add(region, ret);
52654                 break;
52655             
52656             case 'NestedLayoutPanel': 
52657                 // create a new Layout (which is  a Border Layout...
52658                 var el = this.el.createChild();
52659                 var clayout = cfg.layout;
52660                 delete cfg.layout;
52661                 clayout.items   = clayout.items  || [];
52662                 // replace this exitems with the clayout ones..
52663                 xitems = clayout.items;
52664                  
52665                 
52666                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52667                     cfg.background = false;
52668                 }
52669                 var layout = new Roo.BorderLayout(el, clayout);
52670                 
52671                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52672                 //console.log('adding nested layout panel '  + cfg.toSource());
52673                 this.add(region, ret);
52674                 nb = {}; /// find first...
52675                 break;
52676                 
52677             case 'GridPanel': 
52678             
52679                 // needs grid and region
52680                 
52681                 //var el = this.getRegion(region).el.createChild();
52682                 var el = this.el.createChild();
52683                 // create the grid first...
52684                 
52685                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52686                 delete cfg.grid;
52687                 if (region == 'center' && this.active ) {
52688                     cfg.background = false;
52689                 }
52690                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52691                 
52692                 this.add(region, ret);
52693                 if (cfg.background) {
52694                     ret.on('activate', function(gp) {
52695                         if (!gp.grid.rendered) {
52696                             gp.grid.render();
52697                         }
52698                     });
52699                 } else {
52700                     grid.render();
52701                 }
52702                 break;
52703            
52704            
52705            
52706                 
52707                 
52708                 
52709             default:
52710                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52711                     
52712                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52713                     this.add(region, ret);
52714                 } else {
52715                 
52716                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52717                     return null;
52718                 }
52719                 
52720              // GridPanel (grid, cfg)
52721             
52722         }
52723         this.beginUpdate();
52724         // add children..
52725         var region = '';
52726         var abn = {};
52727         Roo.each(xitems, function(i)  {
52728             region = nb && i.region ? i.region : false;
52729             
52730             var add = ret.addxtype(i);
52731            
52732             if (region) {
52733                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52734                 if (!i.background) {
52735                     abn[region] = nb[region] ;
52736                 }
52737             }
52738             
52739         });
52740         this.endUpdate();
52741
52742         // make the last non-background panel active..
52743         //if (nb) { Roo.log(abn); }
52744         if (nb) {
52745             
52746             for(var r in abn) {
52747                 region = this.getRegion(r);
52748                 if (region) {
52749                     // tried using nb[r], but it does not work..
52750                      
52751                     region.showPanel(abn[r]);
52752                    
52753                 }
52754             }
52755         }
52756         return ret;
52757         
52758     }
52759 });
52760
52761 /**
52762  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52763  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52764  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52765  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52766  * <pre><code>
52767 // shorthand
52768 var CP = Roo.ContentPanel;
52769
52770 var layout = Roo.BorderLayout.create({
52771     north: {
52772         initialSize: 25,
52773         titlebar: false,
52774         panels: [new CP("north", "North")]
52775     },
52776     west: {
52777         split:true,
52778         initialSize: 200,
52779         minSize: 175,
52780         maxSize: 400,
52781         titlebar: true,
52782         collapsible: true,
52783         panels: [new CP("west", {title: "West"})]
52784     },
52785     east: {
52786         split:true,
52787         initialSize: 202,
52788         minSize: 175,
52789         maxSize: 400,
52790         titlebar: true,
52791         collapsible: true,
52792         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52793     },
52794     south: {
52795         split:true,
52796         initialSize: 100,
52797         minSize: 100,
52798         maxSize: 200,
52799         titlebar: true,
52800         collapsible: true,
52801         panels: [new CP("south", {title: "South", closable: true})]
52802     },
52803     center: {
52804         titlebar: true,
52805         autoScroll:true,
52806         resizeTabs: true,
52807         minTabWidth: 50,
52808         preferredTabWidth: 150,
52809         panels: [
52810             new CP("center1", {title: "Close Me", closable: true}),
52811             new CP("center2", {title: "Center Panel", closable: false})
52812         ]
52813     }
52814 }, document.body);
52815
52816 layout.getRegion("center").showPanel("center1");
52817 </code></pre>
52818  * @param config
52819  * @param targetEl
52820  */
52821 Roo.BorderLayout.create = function(config, targetEl){
52822     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52823     layout.beginUpdate();
52824     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52825     for(var j = 0, jlen = regions.length; j < jlen; j++){
52826         var lr = regions[j];
52827         if(layout.regions[lr] && config[lr].panels){
52828             var r = layout.regions[lr];
52829             var ps = config[lr].panels;
52830             layout.addTypedPanels(r, ps);
52831         }
52832     }
52833     layout.endUpdate();
52834     return layout;
52835 };
52836
52837 // private
52838 Roo.BorderLayout.RegionFactory = {
52839     // private
52840     validRegions : ["north","south","east","west","center"],
52841
52842     // private
52843     create : function(target, mgr, config){
52844         target = target.toLowerCase();
52845         if(config.lightweight || config.basic){
52846             return new Roo.BasicLayoutRegion(mgr, config, target);
52847         }
52848         switch(target){
52849             case "north":
52850                 return new Roo.NorthLayoutRegion(mgr, config);
52851             case "south":
52852                 return new Roo.SouthLayoutRegion(mgr, config);
52853             case "east":
52854                 return new Roo.EastLayoutRegion(mgr, config);
52855             case "west":
52856                 return new Roo.WestLayoutRegion(mgr, config);
52857             case "center":
52858                 return new Roo.CenterLayoutRegion(mgr, config);
52859         }
52860         throw 'Layout region "'+target+'" not supported.';
52861     }
52862 };/*
52863  * Based on:
52864  * Ext JS Library 1.1.1
52865  * Copyright(c) 2006-2007, Ext JS, LLC.
52866  *
52867  * Originally Released Under LGPL - original licence link has changed is not relivant.
52868  *
52869  * Fork - LGPL
52870  * <script type="text/javascript">
52871  */
52872  
52873 /**
52874  * @class Roo.BasicLayoutRegion
52875  * @extends Roo.util.Observable
52876  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52877  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52878  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52879  */
52880 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52881     this.mgr = mgr;
52882     this.position  = pos;
52883     this.events = {
52884         /**
52885          * @scope Roo.BasicLayoutRegion
52886          */
52887         
52888         /**
52889          * @event beforeremove
52890          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52891          * @param {Roo.LayoutRegion} this
52892          * @param {Roo.ContentPanel} panel The panel
52893          * @param {Object} e The cancel event object
52894          */
52895         "beforeremove" : true,
52896         /**
52897          * @event invalidated
52898          * Fires when the layout for this region is changed.
52899          * @param {Roo.LayoutRegion} this
52900          */
52901         "invalidated" : true,
52902         /**
52903          * @event visibilitychange
52904          * Fires when this region is shown or hidden 
52905          * @param {Roo.LayoutRegion} this
52906          * @param {Boolean} visibility true or false
52907          */
52908         "visibilitychange" : true,
52909         /**
52910          * @event paneladded
52911          * Fires when a panel is added. 
52912          * @param {Roo.LayoutRegion} this
52913          * @param {Roo.ContentPanel} panel The panel
52914          */
52915         "paneladded" : true,
52916         /**
52917          * @event panelremoved
52918          * Fires when a panel is removed. 
52919          * @param {Roo.LayoutRegion} this
52920          * @param {Roo.ContentPanel} panel The panel
52921          */
52922         "panelremoved" : true,
52923         /**
52924          * @event beforecollapse
52925          * Fires when this region before collapse.
52926          * @param {Roo.LayoutRegion} this
52927          */
52928         "beforecollapse" : true,
52929         /**
52930          * @event collapsed
52931          * Fires when this region is collapsed.
52932          * @param {Roo.LayoutRegion} this
52933          */
52934         "collapsed" : true,
52935         /**
52936          * @event expanded
52937          * Fires when this region is expanded.
52938          * @param {Roo.LayoutRegion} this
52939          */
52940         "expanded" : true,
52941         /**
52942          * @event slideshow
52943          * Fires when this region is slid into view.
52944          * @param {Roo.LayoutRegion} this
52945          */
52946         "slideshow" : true,
52947         /**
52948          * @event slidehide
52949          * Fires when this region slides out of view. 
52950          * @param {Roo.LayoutRegion} this
52951          */
52952         "slidehide" : true,
52953         /**
52954          * @event panelactivated
52955          * Fires when a panel is activated. 
52956          * @param {Roo.LayoutRegion} this
52957          * @param {Roo.ContentPanel} panel The activated panel
52958          */
52959         "panelactivated" : true,
52960         /**
52961          * @event resized
52962          * Fires when the user resizes this region. 
52963          * @param {Roo.LayoutRegion} this
52964          * @param {Number} newSize The new size (width for east/west, height for north/south)
52965          */
52966         "resized" : true
52967     };
52968     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52969     this.panels = new Roo.util.MixedCollection();
52970     this.panels.getKey = this.getPanelId.createDelegate(this);
52971     this.box = null;
52972     this.activePanel = null;
52973     // ensure listeners are added...
52974     
52975     if (config.listeners || config.events) {
52976         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52977             listeners : config.listeners || {},
52978             events : config.events || {}
52979         });
52980     }
52981     
52982     if(skipConfig !== true){
52983         this.applyConfig(config);
52984     }
52985 };
52986
52987 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52988     getPanelId : function(p){
52989         return p.getId();
52990     },
52991     
52992     applyConfig : function(config){
52993         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52994         this.config = config;
52995         
52996     },
52997     
52998     /**
52999      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53000      * the width, for horizontal (north, south) the height.
53001      * @param {Number} newSize The new width or height
53002      */
53003     resizeTo : function(newSize){
53004         var el = this.el ? this.el :
53005                  (this.activePanel ? this.activePanel.getEl() : null);
53006         if(el){
53007             switch(this.position){
53008                 case "east":
53009                 case "west":
53010                     el.setWidth(newSize);
53011                     this.fireEvent("resized", this, newSize);
53012                 break;
53013                 case "north":
53014                 case "south":
53015                     el.setHeight(newSize);
53016                     this.fireEvent("resized", this, newSize);
53017                 break;                
53018             }
53019         }
53020     },
53021     
53022     getBox : function(){
53023         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53024     },
53025     
53026     getMargins : function(){
53027         return this.margins;
53028     },
53029     
53030     updateBox : function(box){
53031         this.box = box;
53032         var el = this.activePanel.getEl();
53033         el.dom.style.left = box.x + "px";
53034         el.dom.style.top = box.y + "px";
53035         this.activePanel.setSize(box.width, box.height);
53036     },
53037     
53038     /**
53039      * Returns the container element for this region.
53040      * @return {Roo.Element}
53041      */
53042     getEl : function(){
53043         return this.activePanel;
53044     },
53045     
53046     /**
53047      * Returns true if this region is currently visible.
53048      * @return {Boolean}
53049      */
53050     isVisible : function(){
53051         return this.activePanel ? true : false;
53052     },
53053     
53054     setActivePanel : function(panel){
53055         panel = this.getPanel(panel);
53056         if(this.activePanel && this.activePanel != panel){
53057             this.activePanel.setActiveState(false);
53058             this.activePanel.getEl().setLeftTop(-10000,-10000);
53059         }
53060         this.activePanel = panel;
53061         panel.setActiveState(true);
53062         if(this.box){
53063             panel.setSize(this.box.width, this.box.height);
53064         }
53065         this.fireEvent("panelactivated", this, panel);
53066         this.fireEvent("invalidated");
53067     },
53068     
53069     /**
53070      * Show the specified panel.
53071      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53072      * @return {Roo.ContentPanel} The shown panel or null
53073      */
53074     showPanel : function(panel){
53075         if(panel = this.getPanel(panel)){
53076             this.setActivePanel(panel);
53077         }
53078         return panel;
53079     },
53080     
53081     /**
53082      * Get the active panel for this region.
53083      * @return {Roo.ContentPanel} The active panel or null
53084      */
53085     getActivePanel : function(){
53086         return this.activePanel;
53087     },
53088     
53089     /**
53090      * Add the passed ContentPanel(s)
53091      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53092      * @return {Roo.ContentPanel} The panel added (if only one was added)
53093      */
53094     add : function(panel){
53095         if(arguments.length > 1){
53096             for(var i = 0, len = arguments.length; i < len; i++) {
53097                 this.add(arguments[i]);
53098             }
53099             return null;
53100         }
53101         if(this.hasPanel(panel)){
53102             this.showPanel(panel);
53103             return panel;
53104         }
53105         var el = panel.getEl();
53106         if(el.dom.parentNode != this.mgr.el.dom){
53107             this.mgr.el.dom.appendChild(el.dom);
53108         }
53109         if(panel.setRegion){
53110             panel.setRegion(this);
53111         }
53112         this.panels.add(panel);
53113         el.setStyle("position", "absolute");
53114         if(!panel.background){
53115             this.setActivePanel(panel);
53116             if(this.config.initialSize && this.panels.getCount()==1){
53117                 this.resizeTo(this.config.initialSize);
53118             }
53119         }
53120         this.fireEvent("paneladded", this, panel);
53121         return panel;
53122     },
53123     
53124     /**
53125      * Returns true if the panel is in this region.
53126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53127      * @return {Boolean}
53128      */
53129     hasPanel : function(panel){
53130         if(typeof panel == "object"){ // must be panel obj
53131             panel = panel.getId();
53132         }
53133         return this.getPanel(panel) ? true : false;
53134     },
53135     
53136     /**
53137      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53139      * @param {Boolean} preservePanel Overrides the config preservePanel option
53140      * @return {Roo.ContentPanel} The panel that was removed
53141      */
53142     remove : function(panel, preservePanel){
53143         panel = this.getPanel(panel);
53144         if(!panel){
53145             return null;
53146         }
53147         var e = {};
53148         this.fireEvent("beforeremove", this, panel, e);
53149         if(e.cancel === true){
53150             return null;
53151         }
53152         var panelId = panel.getId();
53153         this.panels.removeKey(panelId);
53154         return panel;
53155     },
53156     
53157     /**
53158      * Returns the panel specified or null if it's not in this region.
53159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53160      * @return {Roo.ContentPanel}
53161      */
53162     getPanel : function(id){
53163         if(typeof id == "object"){ // must be panel obj
53164             return id;
53165         }
53166         return this.panels.get(id);
53167     },
53168     
53169     /**
53170      * Returns this regions position (north/south/east/west/center).
53171      * @return {String} 
53172      */
53173     getPosition: function(){
53174         return this.position;    
53175     }
53176 });/*
53177  * Based on:
53178  * Ext JS Library 1.1.1
53179  * Copyright(c) 2006-2007, Ext JS, LLC.
53180  *
53181  * Originally Released Under LGPL - original licence link has changed is not relivant.
53182  *
53183  * Fork - LGPL
53184  * <script type="text/javascript">
53185  */
53186  
53187 /**
53188  * @class Roo.LayoutRegion
53189  * @extends Roo.BasicLayoutRegion
53190  * This class represents a region in a layout manager.
53191  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53192  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53193  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53194  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53195  * @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})
53196  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53197  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53198  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53199  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53200  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53201  * @cfg {String}    title           The title for the region (overrides panel titles)
53202  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53203  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53204  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53205  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53206  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53207  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53208  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53209  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53210  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53211  * @cfg {Boolean}   showPin         True to show a pin button
53212  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53213  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53214  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53215  * @cfg {Number}    width           For East/West panels
53216  * @cfg {Number}    height          For North/South panels
53217  * @cfg {Boolean}   split           To show the splitter
53218  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53219  */
53220 Roo.LayoutRegion = function(mgr, config, pos){
53221     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53222     var dh = Roo.DomHelper;
53223     /** This region's container element 
53224     * @type Roo.Element */
53225     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53226     /** This region's title element 
53227     * @type Roo.Element */
53228
53229     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53230         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53231         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53232     ]}, true);
53233     this.titleEl.enableDisplayMode();
53234     /** This region's title text element 
53235     * @type HTMLElement */
53236     this.titleTextEl = this.titleEl.dom.firstChild;
53237     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53238     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53239     this.closeBtn.enableDisplayMode();
53240     this.closeBtn.on("click", this.closeClicked, this);
53241     this.closeBtn.hide();
53242
53243     this.createBody(config);
53244     this.visible = true;
53245     this.collapsed = false;
53246
53247     if(config.hideWhenEmpty){
53248         this.hide();
53249         this.on("paneladded", this.validateVisibility, this);
53250         this.on("panelremoved", this.validateVisibility, this);
53251     }
53252     this.applyConfig(config);
53253 };
53254
53255 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53256
53257     createBody : function(){
53258         /** This region's body element 
53259         * @type Roo.Element */
53260         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53261     },
53262
53263     applyConfig : function(c){
53264         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53265             var dh = Roo.DomHelper;
53266             if(c.titlebar !== false){
53267                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53268                 this.collapseBtn.on("click", this.collapse, this);
53269                 this.collapseBtn.enableDisplayMode();
53270
53271                 if(c.showPin === true || this.showPin){
53272                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53273                     this.stickBtn.enableDisplayMode();
53274                     this.stickBtn.on("click", this.expand, this);
53275                     this.stickBtn.hide();
53276                 }
53277             }
53278             /** This region's collapsed element
53279             * @type Roo.Element */
53280             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53281                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53282             ]}, true);
53283             if(c.floatable !== false){
53284                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53285                this.collapsedEl.on("click", this.collapseClick, this);
53286             }
53287
53288             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53289                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53290                    id: "message", unselectable: "on", style:{"float":"left"}});
53291                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53292              }
53293             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53294             this.expandBtn.on("click", this.expand, this);
53295         }
53296         if(this.collapseBtn){
53297             this.collapseBtn.setVisible(c.collapsible == true);
53298         }
53299         this.cmargins = c.cmargins || this.cmargins ||
53300                          (this.position == "west" || this.position == "east" ?
53301                              {top: 0, left: 2, right:2, bottom: 0} :
53302                              {top: 2, left: 0, right:0, bottom: 2});
53303         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53304         this.bottomTabs = c.tabPosition != "top";
53305         this.autoScroll = c.autoScroll || false;
53306         if(this.autoScroll){
53307             this.bodyEl.setStyle("overflow", "auto");
53308         }else{
53309             this.bodyEl.setStyle("overflow", "hidden");
53310         }
53311         //if(c.titlebar !== false){
53312             if((!c.titlebar && !c.title) || c.titlebar === false){
53313                 this.titleEl.hide();
53314             }else{
53315                 this.titleEl.show();
53316                 if(c.title){
53317                     this.titleTextEl.innerHTML = c.title;
53318                 }
53319             }
53320         //}
53321         this.duration = c.duration || .30;
53322         this.slideDuration = c.slideDuration || .45;
53323         this.config = c;
53324         if(c.collapsed){
53325             this.collapse(true);
53326         }
53327         if(c.hidden){
53328             this.hide();
53329         }
53330     },
53331     /**
53332      * Returns true if this region is currently visible.
53333      * @return {Boolean}
53334      */
53335     isVisible : function(){
53336         return this.visible;
53337     },
53338
53339     /**
53340      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53341      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53342      */
53343     setCollapsedTitle : function(title){
53344         title = title || "&#160;";
53345         if(this.collapsedTitleTextEl){
53346             this.collapsedTitleTextEl.innerHTML = title;
53347         }
53348     },
53349
53350     getBox : function(){
53351         var b;
53352         if(!this.collapsed){
53353             b = this.el.getBox(false, true);
53354         }else{
53355             b = this.collapsedEl.getBox(false, true);
53356         }
53357         return b;
53358     },
53359
53360     getMargins : function(){
53361         return this.collapsed ? this.cmargins : this.margins;
53362     },
53363
53364     highlight : function(){
53365         this.el.addClass("x-layout-panel-dragover");
53366     },
53367
53368     unhighlight : function(){
53369         this.el.removeClass("x-layout-panel-dragover");
53370     },
53371
53372     updateBox : function(box){
53373         this.box = box;
53374         if(!this.collapsed){
53375             this.el.dom.style.left = box.x + "px";
53376             this.el.dom.style.top = box.y + "px";
53377             this.updateBody(box.width, box.height);
53378         }else{
53379             this.collapsedEl.dom.style.left = box.x + "px";
53380             this.collapsedEl.dom.style.top = box.y + "px";
53381             this.collapsedEl.setSize(box.width, box.height);
53382         }
53383         if(this.tabs){
53384             this.tabs.autoSizeTabs();
53385         }
53386     },
53387
53388     updateBody : function(w, h){
53389         if(w !== null){
53390             this.el.setWidth(w);
53391             w -= this.el.getBorderWidth("rl");
53392             if(this.config.adjustments){
53393                 w += this.config.adjustments[0];
53394             }
53395         }
53396         if(h !== null){
53397             this.el.setHeight(h);
53398             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53399             h -= this.el.getBorderWidth("tb");
53400             if(this.config.adjustments){
53401                 h += this.config.adjustments[1];
53402             }
53403             this.bodyEl.setHeight(h);
53404             if(this.tabs){
53405                 h = this.tabs.syncHeight(h);
53406             }
53407         }
53408         if(this.panelSize){
53409             w = w !== null ? w : this.panelSize.width;
53410             h = h !== null ? h : this.panelSize.height;
53411         }
53412         if(this.activePanel){
53413             var el = this.activePanel.getEl();
53414             w = w !== null ? w : el.getWidth();
53415             h = h !== null ? h : el.getHeight();
53416             this.panelSize = {width: w, height: h};
53417             this.activePanel.setSize(w, h);
53418         }
53419         if(Roo.isIE && this.tabs){
53420             this.tabs.el.repaint();
53421         }
53422     },
53423
53424     /**
53425      * Returns the container element for this region.
53426      * @return {Roo.Element}
53427      */
53428     getEl : function(){
53429         return this.el;
53430     },
53431
53432     /**
53433      * Hides this region.
53434      */
53435     hide : function(){
53436         if(!this.collapsed){
53437             this.el.dom.style.left = "-2000px";
53438             this.el.hide();
53439         }else{
53440             this.collapsedEl.dom.style.left = "-2000px";
53441             this.collapsedEl.hide();
53442         }
53443         this.visible = false;
53444         this.fireEvent("visibilitychange", this, false);
53445     },
53446
53447     /**
53448      * Shows this region if it was previously hidden.
53449      */
53450     show : function(){
53451         if(!this.collapsed){
53452             this.el.show();
53453         }else{
53454             this.collapsedEl.show();
53455         }
53456         this.visible = true;
53457         this.fireEvent("visibilitychange", this, true);
53458     },
53459
53460     closeClicked : function(){
53461         if(this.activePanel){
53462             this.remove(this.activePanel);
53463         }
53464     },
53465
53466     collapseClick : function(e){
53467         if(this.isSlid){
53468            e.stopPropagation();
53469            this.slideIn();
53470         }else{
53471            e.stopPropagation();
53472            this.slideOut();
53473         }
53474     },
53475
53476     /**
53477      * Collapses this region.
53478      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53479      */
53480     collapse : function(skipAnim, skipCheck){
53481         if(this.collapsed) {
53482             return;
53483         }
53484         
53485         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53486             
53487             this.collapsed = true;
53488             if(this.split){
53489                 this.split.el.hide();
53490             }
53491             if(this.config.animate && skipAnim !== true){
53492                 this.fireEvent("invalidated", this);
53493                 this.animateCollapse();
53494             }else{
53495                 this.el.setLocation(-20000,-20000);
53496                 this.el.hide();
53497                 this.collapsedEl.show();
53498                 this.fireEvent("collapsed", this);
53499                 this.fireEvent("invalidated", this);
53500             }
53501         }
53502         
53503     },
53504
53505     animateCollapse : function(){
53506         // overridden
53507     },
53508
53509     /**
53510      * Expands this region if it was previously collapsed.
53511      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53512      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53513      */
53514     expand : function(e, skipAnim){
53515         if(e) {
53516             e.stopPropagation();
53517         }
53518         if(!this.collapsed || this.el.hasActiveFx()) {
53519             return;
53520         }
53521         if(this.isSlid){
53522             this.afterSlideIn();
53523             skipAnim = true;
53524         }
53525         this.collapsed = false;
53526         if(this.config.animate && skipAnim !== true){
53527             this.animateExpand();
53528         }else{
53529             this.el.show();
53530             if(this.split){
53531                 this.split.el.show();
53532             }
53533             this.collapsedEl.setLocation(-2000,-2000);
53534             this.collapsedEl.hide();
53535             this.fireEvent("invalidated", this);
53536             this.fireEvent("expanded", this);
53537         }
53538     },
53539
53540     animateExpand : function(){
53541         // overridden
53542     },
53543
53544     initTabs : function()
53545     {
53546         this.bodyEl.setStyle("overflow", "hidden");
53547         var ts = new Roo.TabPanel(
53548                 this.bodyEl.dom,
53549                 {
53550                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53551                     disableTooltips: this.config.disableTabTips,
53552                     toolbar : this.config.toolbar
53553                 }
53554         );
53555         if(this.config.hideTabs){
53556             ts.stripWrap.setDisplayed(false);
53557         }
53558         this.tabs = ts;
53559         ts.resizeTabs = this.config.resizeTabs === true;
53560         ts.minTabWidth = this.config.minTabWidth || 40;
53561         ts.maxTabWidth = this.config.maxTabWidth || 250;
53562         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53563         ts.monitorResize = false;
53564         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53565         ts.bodyEl.addClass('x-layout-tabs-body');
53566         this.panels.each(this.initPanelAsTab, this);
53567     },
53568
53569     initPanelAsTab : function(panel){
53570         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53571                     this.config.closeOnTab && panel.isClosable());
53572         if(panel.tabTip !== undefined){
53573             ti.setTooltip(panel.tabTip);
53574         }
53575         ti.on("activate", function(){
53576               this.setActivePanel(panel);
53577         }, this);
53578         if(this.config.closeOnTab){
53579             ti.on("beforeclose", function(t, e){
53580                 e.cancel = true;
53581                 this.remove(panel);
53582             }, this);
53583         }
53584         return ti;
53585     },
53586
53587     updatePanelTitle : function(panel, title){
53588         if(this.activePanel == panel){
53589             this.updateTitle(title);
53590         }
53591         if(this.tabs){
53592             var ti = this.tabs.getTab(panel.getEl().id);
53593             ti.setText(title);
53594             if(panel.tabTip !== undefined){
53595                 ti.setTooltip(panel.tabTip);
53596             }
53597         }
53598     },
53599
53600     updateTitle : function(title){
53601         if(this.titleTextEl && !this.config.title){
53602             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53603         }
53604     },
53605
53606     setActivePanel : function(panel){
53607         panel = this.getPanel(panel);
53608         if(this.activePanel && this.activePanel != panel){
53609             this.activePanel.setActiveState(false);
53610         }
53611         this.activePanel = panel;
53612         panel.setActiveState(true);
53613         if(this.panelSize){
53614             panel.setSize(this.panelSize.width, this.panelSize.height);
53615         }
53616         if(this.closeBtn){
53617             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53618         }
53619         this.updateTitle(panel.getTitle());
53620         if(this.tabs){
53621             this.fireEvent("invalidated", this);
53622         }
53623         this.fireEvent("panelactivated", this, panel);
53624     },
53625
53626     /**
53627      * Shows the specified panel.
53628      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53629      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53630      */
53631     showPanel : function(panel)
53632     {
53633         panel = this.getPanel(panel);
53634         if(panel){
53635             if(this.tabs){
53636                 var tab = this.tabs.getTab(panel.getEl().id);
53637                 if(tab.isHidden()){
53638                     this.tabs.unhideTab(tab.id);
53639                 }
53640                 tab.activate();
53641             }else{
53642                 this.setActivePanel(panel);
53643             }
53644         }
53645         return panel;
53646     },
53647
53648     /**
53649      * Get the active panel for this region.
53650      * @return {Roo.ContentPanel} The active panel or null
53651      */
53652     getActivePanel : function(){
53653         return this.activePanel;
53654     },
53655
53656     validateVisibility : function(){
53657         if(this.panels.getCount() < 1){
53658             this.updateTitle("&#160;");
53659             this.closeBtn.hide();
53660             this.hide();
53661         }else{
53662             if(!this.isVisible()){
53663                 this.show();
53664             }
53665         }
53666     },
53667
53668     /**
53669      * Adds the passed ContentPanel(s) to this region.
53670      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53671      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53672      */
53673     add : function(panel){
53674         if(arguments.length > 1){
53675             for(var i = 0, len = arguments.length; i < len; i++) {
53676                 this.add(arguments[i]);
53677             }
53678             return null;
53679         }
53680         if(this.hasPanel(panel)){
53681             this.showPanel(panel);
53682             return panel;
53683         }
53684         panel.setRegion(this);
53685         this.panels.add(panel);
53686         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53687             this.bodyEl.dom.appendChild(panel.getEl().dom);
53688             if(panel.background !== true){
53689                 this.setActivePanel(panel);
53690             }
53691             this.fireEvent("paneladded", this, panel);
53692             return panel;
53693         }
53694         if(!this.tabs){
53695             this.initTabs();
53696         }else{
53697             this.initPanelAsTab(panel);
53698         }
53699         if(panel.background !== true){
53700             this.tabs.activate(panel.getEl().id);
53701         }
53702         this.fireEvent("paneladded", this, panel);
53703         return panel;
53704     },
53705
53706     /**
53707      * Hides the tab for the specified panel.
53708      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53709      */
53710     hidePanel : function(panel){
53711         if(this.tabs && (panel = this.getPanel(panel))){
53712             this.tabs.hideTab(panel.getEl().id);
53713         }
53714     },
53715
53716     /**
53717      * Unhides the tab for a previously hidden panel.
53718      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53719      */
53720     unhidePanel : function(panel){
53721         if(this.tabs && (panel = this.getPanel(panel))){
53722             this.tabs.unhideTab(panel.getEl().id);
53723         }
53724     },
53725
53726     clearPanels : function(){
53727         while(this.panels.getCount() > 0){
53728              this.remove(this.panels.first());
53729         }
53730     },
53731
53732     /**
53733      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53735      * @param {Boolean} preservePanel Overrides the config preservePanel option
53736      * @return {Roo.ContentPanel} The panel that was removed
53737      */
53738     remove : function(panel, preservePanel){
53739         panel = this.getPanel(panel);
53740         if(!panel){
53741             return null;
53742         }
53743         var e = {};
53744         this.fireEvent("beforeremove", this, panel, e);
53745         if(e.cancel === true){
53746             return null;
53747         }
53748         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53749         var panelId = panel.getId();
53750         this.panels.removeKey(panelId);
53751         if(preservePanel){
53752             document.body.appendChild(panel.getEl().dom);
53753         }
53754         if(this.tabs){
53755             this.tabs.removeTab(panel.getEl().id);
53756         }else if (!preservePanel){
53757             this.bodyEl.dom.removeChild(panel.getEl().dom);
53758         }
53759         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53760             var p = this.panels.first();
53761             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53762             tempEl.appendChild(p.getEl().dom);
53763             this.bodyEl.update("");
53764             this.bodyEl.dom.appendChild(p.getEl().dom);
53765             tempEl = null;
53766             this.updateTitle(p.getTitle());
53767             this.tabs = null;
53768             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53769             this.setActivePanel(p);
53770         }
53771         panel.setRegion(null);
53772         if(this.activePanel == panel){
53773             this.activePanel = null;
53774         }
53775         if(this.config.autoDestroy !== false && preservePanel !== true){
53776             try{panel.destroy();}catch(e){}
53777         }
53778         this.fireEvent("panelremoved", this, panel);
53779         return panel;
53780     },
53781
53782     /**
53783      * Returns the TabPanel component used by this region
53784      * @return {Roo.TabPanel}
53785      */
53786     getTabs : function(){
53787         return this.tabs;
53788     },
53789
53790     createTool : function(parentEl, className){
53791         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53792             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53793         btn.addClassOnOver("x-layout-tools-button-over");
53794         return btn;
53795     }
53796 });/*
53797  * Based on:
53798  * Ext JS Library 1.1.1
53799  * Copyright(c) 2006-2007, Ext JS, LLC.
53800  *
53801  * Originally Released Under LGPL - original licence link has changed is not relivant.
53802  *
53803  * Fork - LGPL
53804  * <script type="text/javascript">
53805  */
53806  
53807
53808
53809 /**
53810  * @class Roo.SplitLayoutRegion
53811  * @extends Roo.LayoutRegion
53812  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53813  */
53814 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53815     this.cursor = cursor;
53816     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53817 };
53818
53819 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53820     splitTip : "Drag to resize.",
53821     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53822     useSplitTips : false,
53823
53824     applyConfig : function(config){
53825         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53826         if(config.split){
53827             if(!this.split){
53828                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53829                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53830                 /** The SplitBar for this region 
53831                 * @type Roo.SplitBar */
53832                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53833                 this.split.on("moved", this.onSplitMove, this);
53834                 this.split.useShim = config.useShim === true;
53835                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53836                 if(this.useSplitTips){
53837                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53838                 }
53839                 if(config.collapsible){
53840                     this.split.el.on("dblclick", this.collapse,  this);
53841                 }
53842             }
53843             if(typeof config.minSize != "undefined"){
53844                 this.split.minSize = config.minSize;
53845             }
53846             if(typeof config.maxSize != "undefined"){
53847                 this.split.maxSize = config.maxSize;
53848             }
53849             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53850                 this.hideSplitter();
53851             }
53852         }
53853     },
53854
53855     getHMaxSize : function(){
53856          var cmax = this.config.maxSize || 10000;
53857          var center = this.mgr.getRegion("center");
53858          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53859     },
53860
53861     getVMaxSize : function(){
53862          var cmax = this.config.maxSize || 10000;
53863          var center = this.mgr.getRegion("center");
53864          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53865     },
53866
53867     onSplitMove : function(split, newSize){
53868         this.fireEvent("resized", this, newSize);
53869     },
53870     
53871     /** 
53872      * Returns the {@link Roo.SplitBar} for this region.
53873      * @return {Roo.SplitBar}
53874      */
53875     getSplitBar : function(){
53876         return this.split;
53877     },
53878     
53879     hide : function(){
53880         this.hideSplitter();
53881         Roo.SplitLayoutRegion.superclass.hide.call(this);
53882     },
53883
53884     hideSplitter : function(){
53885         if(this.split){
53886             this.split.el.setLocation(-2000,-2000);
53887             this.split.el.hide();
53888         }
53889     },
53890
53891     show : function(){
53892         if(this.split){
53893             this.split.el.show();
53894         }
53895         Roo.SplitLayoutRegion.superclass.show.call(this);
53896     },
53897     
53898     beforeSlide: function(){
53899         if(Roo.isGecko){// firefox overflow auto bug workaround
53900             this.bodyEl.clip();
53901             if(this.tabs) {
53902                 this.tabs.bodyEl.clip();
53903             }
53904             if(this.activePanel){
53905                 this.activePanel.getEl().clip();
53906                 
53907                 if(this.activePanel.beforeSlide){
53908                     this.activePanel.beforeSlide();
53909                 }
53910             }
53911         }
53912     },
53913     
53914     afterSlide : function(){
53915         if(Roo.isGecko){// firefox overflow auto bug workaround
53916             this.bodyEl.unclip();
53917             if(this.tabs) {
53918                 this.tabs.bodyEl.unclip();
53919             }
53920             if(this.activePanel){
53921                 this.activePanel.getEl().unclip();
53922                 if(this.activePanel.afterSlide){
53923                     this.activePanel.afterSlide();
53924                 }
53925             }
53926         }
53927     },
53928
53929     initAutoHide : function(){
53930         if(this.autoHide !== false){
53931             if(!this.autoHideHd){
53932                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53933                 this.autoHideHd = {
53934                     "mouseout": function(e){
53935                         if(!e.within(this.el, true)){
53936                             st.delay(500);
53937                         }
53938                     },
53939                     "mouseover" : function(e){
53940                         st.cancel();
53941                     },
53942                     scope : this
53943                 };
53944             }
53945             this.el.on(this.autoHideHd);
53946         }
53947     },
53948
53949     clearAutoHide : function(){
53950         if(this.autoHide !== false){
53951             this.el.un("mouseout", this.autoHideHd.mouseout);
53952             this.el.un("mouseover", this.autoHideHd.mouseover);
53953         }
53954     },
53955
53956     clearMonitor : function(){
53957         Roo.get(document).un("click", this.slideInIf, this);
53958     },
53959
53960     // these names are backwards but not changed for compat
53961     slideOut : function(){
53962         if(this.isSlid || this.el.hasActiveFx()){
53963             return;
53964         }
53965         this.isSlid = true;
53966         if(this.collapseBtn){
53967             this.collapseBtn.hide();
53968         }
53969         this.closeBtnState = this.closeBtn.getStyle('display');
53970         this.closeBtn.hide();
53971         if(this.stickBtn){
53972             this.stickBtn.show();
53973         }
53974         this.el.show();
53975         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53976         this.beforeSlide();
53977         this.el.setStyle("z-index", 10001);
53978         this.el.slideIn(this.getSlideAnchor(), {
53979             callback: function(){
53980                 this.afterSlide();
53981                 this.initAutoHide();
53982                 Roo.get(document).on("click", this.slideInIf, this);
53983                 this.fireEvent("slideshow", this);
53984             },
53985             scope: this,
53986             block: true
53987         });
53988     },
53989
53990     afterSlideIn : function(){
53991         this.clearAutoHide();
53992         this.isSlid = false;
53993         this.clearMonitor();
53994         this.el.setStyle("z-index", "");
53995         if(this.collapseBtn){
53996             this.collapseBtn.show();
53997         }
53998         this.closeBtn.setStyle('display', this.closeBtnState);
53999         if(this.stickBtn){
54000             this.stickBtn.hide();
54001         }
54002         this.fireEvent("slidehide", this);
54003     },
54004
54005     slideIn : function(cb){
54006         if(!this.isSlid || this.el.hasActiveFx()){
54007             Roo.callback(cb);
54008             return;
54009         }
54010         this.isSlid = false;
54011         this.beforeSlide();
54012         this.el.slideOut(this.getSlideAnchor(), {
54013             callback: function(){
54014                 this.el.setLeftTop(-10000, -10000);
54015                 this.afterSlide();
54016                 this.afterSlideIn();
54017                 Roo.callback(cb);
54018             },
54019             scope: this,
54020             block: true
54021         });
54022     },
54023     
54024     slideInIf : function(e){
54025         if(!e.within(this.el)){
54026             this.slideIn();
54027         }
54028     },
54029
54030     animateCollapse : function(){
54031         this.beforeSlide();
54032         this.el.setStyle("z-index", 20000);
54033         var anchor = this.getSlideAnchor();
54034         this.el.slideOut(anchor, {
54035             callback : function(){
54036                 this.el.setStyle("z-index", "");
54037                 this.collapsedEl.slideIn(anchor, {duration:.3});
54038                 this.afterSlide();
54039                 this.el.setLocation(-10000,-10000);
54040                 this.el.hide();
54041                 this.fireEvent("collapsed", this);
54042             },
54043             scope: this,
54044             block: true
54045         });
54046     },
54047
54048     animateExpand : function(){
54049         this.beforeSlide();
54050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54051         this.el.setStyle("z-index", 20000);
54052         this.collapsedEl.hide({
54053             duration:.1
54054         });
54055         this.el.slideIn(this.getSlideAnchor(), {
54056             callback : function(){
54057                 this.el.setStyle("z-index", "");
54058                 this.afterSlide();
54059                 if(this.split){
54060                     this.split.el.show();
54061                 }
54062                 this.fireEvent("invalidated", this);
54063                 this.fireEvent("expanded", this);
54064             },
54065             scope: this,
54066             block: true
54067         });
54068     },
54069
54070     anchors : {
54071         "west" : "left",
54072         "east" : "right",
54073         "north" : "top",
54074         "south" : "bottom"
54075     },
54076
54077     sanchors : {
54078         "west" : "l",
54079         "east" : "r",
54080         "north" : "t",
54081         "south" : "b"
54082     },
54083
54084     canchors : {
54085         "west" : "tl-tr",
54086         "east" : "tr-tl",
54087         "north" : "tl-bl",
54088         "south" : "bl-tl"
54089     },
54090
54091     getAnchor : function(){
54092         return this.anchors[this.position];
54093     },
54094
54095     getCollapseAnchor : function(){
54096         return this.canchors[this.position];
54097     },
54098
54099     getSlideAnchor : function(){
54100         return this.sanchors[this.position];
54101     },
54102
54103     getAlignAdj : function(){
54104         var cm = this.cmargins;
54105         switch(this.position){
54106             case "west":
54107                 return [0, 0];
54108             break;
54109             case "east":
54110                 return [0, 0];
54111             break;
54112             case "north":
54113                 return [0, 0];
54114             break;
54115             case "south":
54116                 return [0, 0];
54117             break;
54118         }
54119     },
54120
54121     getExpandAdj : function(){
54122         var c = this.collapsedEl, cm = this.cmargins;
54123         switch(this.position){
54124             case "west":
54125                 return [-(cm.right+c.getWidth()+cm.left), 0];
54126             break;
54127             case "east":
54128                 return [cm.right+c.getWidth()+cm.left, 0];
54129             break;
54130             case "north":
54131                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54132             break;
54133             case "south":
54134                 return [0, cm.top+cm.bottom+c.getHeight()];
54135             break;
54136         }
54137     }
54138 });/*
54139  * Based on:
54140  * Ext JS Library 1.1.1
54141  * Copyright(c) 2006-2007, Ext JS, LLC.
54142  *
54143  * Originally Released Under LGPL - original licence link has changed is not relivant.
54144  *
54145  * Fork - LGPL
54146  * <script type="text/javascript">
54147  */
54148 /*
54149  * These classes are private internal classes
54150  */
54151 Roo.CenterLayoutRegion = function(mgr, config){
54152     Roo.LayoutRegion.call(this, mgr, config, "center");
54153     this.visible = true;
54154     this.minWidth = config.minWidth || 20;
54155     this.minHeight = config.minHeight || 20;
54156 };
54157
54158 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54159     hide : function(){
54160         // center panel can't be hidden
54161     },
54162     
54163     show : function(){
54164         // center panel can't be hidden
54165     },
54166     
54167     getMinWidth: function(){
54168         return this.minWidth;
54169     },
54170     
54171     getMinHeight: function(){
54172         return this.minHeight;
54173     }
54174 });
54175
54176
54177 Roo.NorthLayoutRegion = function(mgr, config){
54178     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54179     if(this.split){
54180         this.split.placement = Roo.SplitBar.TOP;
54181         this.split.orientation = Roo.SplitBar.VERTICAL;
54182         this.split.el.addClass("x-layout-split-v");
54183     }
54184     var size = config.initialSize || config.height;
54185     if(typeof size != "undefined"){
54186         this.el.setHeight(size);
54187     }
54188 };
54189 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54190     orientation: Roo.SplitBar.VERTICAL,
54191     getBox : function(){
54192         if(this.collapsed){
54193             return this.collapsedEl.getBox();
54194         }
54195         var box = this.el.getBox();
54196         if(this.split){
54197             box.height += this.split.el.getHeight();
54198         }
54199         return box;
54200     },
54201     
54202     updateBox : function(box){
54203         if(this.split && !this.collapsed){
54204             box.height -= this.split.el.getHeight();
54205             this.split.el.setLeft(box.x);
54206             this.split.el.setTop(box.y+box.height);
54207             this.split.el.setWidth(box.width);
54208         }
54209         if(this.collapsed){
54210             this.updateBody(box.width, null);
54211         }
54212         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54213     }
54214 });
54215
54216 Roo.SouthLayoutRegion = function(mgr, config){
54217     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54218     if(this.split){
54219         this.split.placement = Roo.SplitBar.BOTTOM;
54220         this.split.orientation = Roo.SplitBar.VERTICAL;
54221         this.split.el.addClass("x-layout-split-v");
54222     }
54223     var size = config.initialSize || config.height;
54224     if(typeof size != "undefined"){
54225         this.el.setHeight(size);
54226     }
54227 };
54228 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54229     orientation: Roo.SplitBar.VERTICAL,
54230     getBox : function(){
54231         if(this.collapsed){
54232             return this.collapsedEl.getBox();
54233         }
54234         var box = this.el.getBox();
54235         if(this.split){
54236             var sh = this.split.el.getHeight();
54237             box.height += sh;
54238             box.y -= sh;
54239         }
54240         return box;
54241     },
54242     
54243     updateBox : function(box){
54244         if(this.split && !this.collapsed){
54245             var sh = this.split.el.getHeight();
54246             box.height -= sh;
54247             box.y += sh;
54248             this.split.el.setLeft(box.x);
54249             this.split.el.setTop(box.y-sh);
54250             this.split.el.setWidth(box.width);
54251         }
54252         if(this.collapsed){
54253             this.updateBody(box.width, null);
54254         }
54255         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54256     }
54257 });
54258
54259 Roo.EastLayoutRegion = function(mgr, config){
54260     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54261     if(this.split){
54262         this.split.placement = Roo.SplitBar.RIGHT;
54263         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54264         this.split.el.addClass("x-layout-split-h");
54265     }
54266     var size = config.initialSize || config.width;
54267     if(typeof size != "undefined"){
54268         this.el.setWidth(size);
54269     }
54270 };
54271 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54272     orientation: Roo.SplitBar.HORIZONTAL,
54273     getBox : function(){
54274         if(this.collapsed){
54275             return this.collapsedEl.getBox();
54276         }
54277         var box = this.el.getBox();
54278         if(this.split){
54279             var sw = this.split.el.getWidth();
54280             box.width += sw;
54281             box.x -= sw;
54282         }
54283         return box;
54284     },
54285
54286     updateBox : function(box){
54287         if(this.split && !this.collapsed){
54288             var sw = this.split.el.getWidth();
54289             box.width -= sw;
54290             this.split.el.setLeft(box.x);
54291             this.split.el.setTop(box.y);
54292             this.split.el.setHeight(box.height);
54293             box.x += sw;
54294         }
54295         if(this.collapsed){
54296             this.updateBody(null, box.height);
54297         }
54298         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54299     }
54300 });
54301
54302 Roo.WestLayoutRegion = function(mgr, config){
54303     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54304     if(this.split){
54305         this.split.placement = Roo.SplitBar.LEFT;
54306         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54307         this.split.el.addClass("x-layout-split-h");
54308     }
54309     var size = config.initialSize || config.width;
54310     if(typeof size != "undefined"){
54311         this.el.setWidth(size);
54312     }
54313 };
54314 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54315     orientation: Roo.SplitBar.HORIZONTAL,
54316     getBox : function(){
54317         if(this.collapsed){
54318             return this.collapsedEl.getBox();
54319         }
54320         var box = this.el.getBox();
54321         if(this.split){
54322             box.width += this.split.el.getWidth();
54323         }
54324         return box;
54325     },
54326     
54327     updateBox : function(box){
54328         if(this.split && !this.collapsed){
54329             var sw = this.split.el.getWidth();
54330             box.width -= sw;
54331             this.split.el.setLeft(box.x+box.width);
54332             this.split.el.setTop(box.y);
54333             this.split.el.setHeight(box.height);
54334         }
54335         if(this.collapsed){
54336             this.updateBody(null, box.height);
54337         }
54338         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54339     }
54340 });
54341 /*
54342  * Based on:
54343  * Ext JS Library 1.1.1
54344  * Copyright(c) 2006-2007, Ext JS, LLC.
54345  *
54346  * Originally Released Under LGPL - original licence link has changed is not relivant.
54347  *
54348  * Fork - LGPL
54349  * <script type="text/javascript">
54350  */
54351  
54352  
54353 /*
54354  * Private internal class for reading and applying state
54355  */
54356 Roo.LayoutStateManager = function(layout){
54357      // default empty state
54358      this.state = {
54359         north: {},
54360         south: {},
54361         east: {},
54362         west: {}       
54363     };
54364 };
54365
54366 Roo.LayoutStateManager.prototype = {
54367     init : function(layout, provider){
54368         this.provider = provider;
54369         var state = provider.get(layout.id+"-layout-state");
54370         if(state){
54371             var wasUpdating = layout.isUpdating();
54372             if(!wasUpdating){
54373                 layout.beginUpdate();
54374             }
54375             for(var key in state){
54376                 if(typeof state[key] != "function"){
54377                     var rstate = state[key];
54378                     var r = layout.getRegion(key);
54379                     if(r && rstate){
54380                         if(rstate.size){
54381                             r.resizeTo(rstate.size);
54382                         }
54383                         if(rstate.collapsed == true){
54384                             r.collapse(true);
54385                         }else{
54386                             r.expand(null, true);
54387                         }
54388                     }
54389                 }
54390             }
54391             if(!wasUpdating){
54392                 layout.endUpdate();
54393             }
54394             this.state = state; 
54395         }
54396         this.layout = layout;
54397         layout.on("regionresized", this.onRegionResized, this);
54398         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54399         layout.on("regionexpanded", this.onRegionExpanded, this);
54400     },
54401     
54402     storeState : function(){
54403         this.provider.set(this.layout.id+"-layout-state", this.state);
54404     },
54405     
54406     onRegionResized : function(region, newSize){
54407         this.state[region.getPosition()].size = newSize;
54408         this.storeState();
54409     },
54410     
54411     onRegionCollapsed : function(region){
54412         this.state[region.getPosition()].collapsed = true;
54413         this.storeState();
54414     },
54415     
54416     onRegionExpanded : function(region){
54417         this.state[region.getPosition()].collapsed = false;
54418         this.storeState();
54419     }
54420 };/*
54421  * Based on:
54422  * Ext JS Library 1.1.1
54423  * Copyright(c) 2006-2007, Ext JS, LLC.
54424  *
54425  * Originally Released Under LGPL - original licence link has changed is not relivant.
54426  *
54427  * Fork - LGPL
54428  * <script type="text/javascript">
54429  */
54430 /**
54431  * @class Roo.ContentPanel
54432  * @extends Roo.util.Observable
54433  * @children Roo.form.Form Roo.JsonView Roo.View
54434  * A basic ContentPanel element.
54435  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54436  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54437  * @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
54438  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54439  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54440  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54441  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
54442  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54443  * @cfg {String} title          The title for this panel
54444  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54445  * @cfg {String} url            Calls {@link #setUrl} with this value
54446  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54447  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54448  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54449  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54450  * @cfg {String}    style  Extra style to add to the content panel
54451  * @cfg {Roo.menu.Menu} menu  popup menu
54452
54453  * @constructor
54454  * Create a new ContentPanel.
54455  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54456  * @param {String/Object} config A string to set only the title or a config object
54457  * @param {String} content (optional) Set the HTML content for this panel
54458  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54459  */
54460 Roo.ContentPanel = function(el, config, content){
54461     
54462      
54463     /*
54464     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54465         config = el;
54466         el = Roo.id();
54467     }
54468     if (config && config.parentLayout) { 
54469         el = config.parentLayout.el.createChild(); 
54470     }
54471     */
54472     if(el.autoCreate){ // xtype is available if this is called from factory
54473         config = el;
54474         el = Roo.id();
54475     }
54476     this.el = Roo.get(el);
54477     if(!this.el && config && config.autoCreate){
54478         if(typeof config.autoCreate == "object"){
54479             if(!config.autoCreate.id){
54480                 config.autoCreate.id = config.id||el;
54481             }
54482             this.el = Roo.DomHelper.append(document.body,
54483                         config.autoCreate, true);
54484         }else{
54485             this.el = Roo.DomHelper.append(document.body,
54486                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54487         }
54488     }
54489     
54490     
54491     this.closable = false;
54492     this.loaded = false;
54493     this.active = false;
54494     if(typeof config == "string"){
54495         this.title = config;
54496     }else{
54497         Roo.apply(this, config);
54498     }
54499     
54500     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54501         this.wrapEl = this.el.wrap();
54502         this.toolbar.container = this.el.insertSibling(false, 'before');
54503         this.toolbar = new Roo.Toolbar(this.toolbar);
54504     }
54505     
54506     // xtype created footer. - not sure if will work as we normally have to render first..
54507     if (this.footer && !this.footer.el && this.footer.xtype) {
54508         if (!this.wrapEl) {
54509             this.wrapEl = this.el.wrap();
54510         }
54511     
54512         this.footer.container = this.wrapEl.createChild();
54513          
54514         this.footer = Roo.factory(this.footer, Roo);
54515         
54516     }
54517     
54518     if(this.resizeEl){
54519         this.resizeEl = Roo.get(this.resizeEl, true);
54520     }else{
54521         this.resizeEl = this.el;
54522     }
54523     // handle view.xtype
54524     
54525  
54526     
54527     
54528     this.addEvents({
54529         /**
54530          * @event activate
54531          * Fires when this panel is activated. 
54532          * @param {Roo.ContentPanel} this
54533          */
54534         "activate" : true,
54535         /**
54536          * @event deactivate
54537          * Fires when this panel is activated. 
54538          * @param {Roo.ContentPanel} this
54539          */
54540         "deactivate" : true,
54541
54542         /**
54543          * @event resize
54544          * Fires when this panel is resized if fitToFrame is true.
54545          * @param {Roo.ContentPanel} this
54546          * @param {Number} width The width after any component adjustments
54547          * @param {Number} height The height after any component adjustments
54548          */
54549         "resize" : true,
54550         
54551          /**
54552          * @event render
54553          * Fires when this tab is created
54554          * @param {Roo.ContentPanel} this
54555          */
54556         "render" : true
54557          
54558         
54559     });
54560     
54561
54562     
54563     
54564     if(this.autoScroll){
54565         this.resizeEl.setStyle("overflow", "auto");
54566     } else {
54567         // fix randome scrolling
54568         this.el.on('scroll', function() {
54569             Roo.log('fix random scolling');
54570             this.scrollTo('top',0); 
54571         });
54572     }
54573     content = content || this.content;
54574     if(content){
54575         this.setContent(content);
54576     }
54577     if(config && config.url){
54578         this.setUrl(this.url, this.params, this.loadOnce);
54579     }
54580     
54581     
54582     
54583     Roo.ContentPanel.superclass.constructor.call(this);
54584     
54585     if (this.view && typeof(this.view.xtype) != 'undefined') {
54586         this.view.el = this.el.appendChild(document.createElement("div"));
54587         this.view = Roo.factory(this.view); 
54588         this.view.render  &&  this.view.render(false, '');  
54589     }
54590     
54591     
54592     this.fireEvent('render', this);
54593 };
54594
54595 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54596     tabTip:'',
54597     setRegion : function(region){
54598         this.region = region;
54599         if(region){
54600            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54601         }else{
54602            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54603         } 
54604     },
54605     
54606     /**
54607      * Returns the toolbar for this Panel if one was configured. 
54608      * @return {Roo.Toolbar} 
54609      */
54610     getToolbar : function(){
54611         return this.toolbar;
54612     },
54613     
54614     setActiveState : function(active){
54615         this.active = active;
54616         if(!active){
54617             this.fireEvent("deactivate", this);
54618         }else{
54619             this.fireEvent("activate", this);
54620         }
54621     },
54622     /**
54623      * Updates this panel's element
54624      * @param {String} content The new content
54625      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54626     */
54627     setContent : function(content, loadScripts){
54628         this.el.update(content, loadScripts);
54629     },
54630
54631     ignoreResize : function(w, h){
54632         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54633             return true;
54634         }else{
54635             this.lastSize = {width: w, height: h};
54636             return false;
54637         }
54638     },
54639     /**
54640      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54641      * @return {Roo.UpdateManager} The UpdateManager
54642      */
54643     getUpdateManager : function(){
54644         return this.el.getUpdateManager();
54645     },
54646      /**
54647      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54648      * @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:
54649 <pre><code>
54650 panel.load({
54651     url: "your-url.php",
54652     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54653     callback: yourFunction,
54654     scope: yourObject, //(optional scope)
54655     discardUrl: false,
54656     nocache: false,
54657     text: "Loading...",
54658     timeout: 30,
54659     scripts: false
54660 });
54661 </code></pre>
54662      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54663      * 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.
54664      * @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}
54665      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54666      * @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.
54667      * @return {Roo.ContentPanel} this
54668      */
54669     load : function(){
54670         var um = this.el.getUpdateManager();
54671         um.update.apply(um, arguments);
54672         return this;
54673     },
54674
54675
54676     /**
54677      * 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.
54678      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54679      * @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)
54680      * @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)
54681      * @return {Roo.UpdateManager} The UpdateManager
54682      */
54683     setUrl : function(url, params, loadOnce){
54684         if(this.refreshDelegate){
54685             this.removeListener("activate", this.refreshDelegate);
54686         }
54687         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54688         this.on("activate", this.refreshDelegate);
54689         return this.el.getUpdateManager();
54690     },
54691     
54692     _handleRefresh : function(url, params, loadOnce){
54693         if(!loadOnce || !this.loaded){
54694             var updater = this.el.getUpdateManager();
54695             updater.update(url, params, this._setLoaded.createDelegate(this));
54696         }
54697     },
54698     
54699     _setLoaded : function(){
54700         this.loaded = true;
54701     }, 
54702     
54703     /**
54704      * Returns this panel's id
54705      * @return {String} 
54706      */
54707     getId : function(){
54708         return this.el.id;
54709     },
54710     
54711     /** 
54712      * Returns this panel's element - used by regiosn to add.
54713      * @return {Roo.Element} 
54714      */
54715     getEl : function(){
54716         return this.wrapEl || this.el;
54717     },
54718     
54719     adjustForComponents : function(width, height)
54720     {
54721         //Roo.log('adjustForComponents ');
54722         if(this.resizeEl != this.el){
54723             width -= this.el.getFrameWidth('lr');
54724             height -= this.el.getFrameWidth('tb');
54725         }
54726         if(this.toolbar){
54727             var te = this.toolbar.getEl();
54728             height -= te.getHeight();
54729             te.setWidth(width);
54730         }
54731         if(this.footer){
54732             var te = this.footer.getEl();
54733             //Roo.log("footer:" + te.getHeight());
54734             
54735             height -= te.getHeight();
54736             te.setWidth(width);
54737         }
54738         
54739         
54740         if(this.adjustments){
54741             width += this.adjustments[0];
54742             height += this.adjustments[1];
54743         }
54744         return {"width": width, "height": height};
54745     },
54746     
54747     setSize : function(width, height){
54748         if(this.fitToFrame && !this.ignoreResize(width, height)){
54749             if(this.fitContainer && this.resizeEl != this.el){
54750                 this.el.setSize(width, height);
54751             }
54752             var size = this.adjustForComponents(width, height);
54753             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54754             this.fireEvent('resize', this, size.width, size.height);
54755         }
54756     },
54757     
54758     /**
54759      * Returns this panel's title
54760      * @return {String} 
54761      */
54762     getTitle : function(){
54763         return this.title;
54764     },
54765     
54766     /**
54767      * Set this panel's title
54768      * @param {String} title
54769      */
54770     setTitle : function(title){
54771         this.title = title;
54772         if(this.region){
54773             this.region.updatePanelTitle(this, title);
54774         }
54775     },
54776     
54777     /**
54778      * Returns true is this panel was configured to be closable
54779      * @return {Boolean} 
54780      */
54781     isClosable : function(){
54782         return this.closable;
54783     },
54784     
54785     beforeSlide : function(){
54786         this.el.clip();
54787         this.resizeEl.clip();
54788     },
54789     
54790     afterSlide : function(){
54791         this.el.unclip();
54792         this.resizeEl.unclip();
54793     },
54794     
54795     /**
54796      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54797      *   Will fail silently if the {@link #setUrl} method has not been called.
54798      *   This does not activate the panel, just updates its content.
54799      */
54800     refresh : function(){
54801         if(this.refreshDelegate){
54802            this.loaded = false;
54803            this.refreshDelegate();
54804         }
54805     },
54806     
54807     /**
54808      * Destroys this panel
54809      */
54810     destroy : function(){
54811         this.el.removeAllListeners();
54812         var tempEl = document.createElement("span");
54813         tempEl.appendChild(this.el.dom);
54814         tempEl.innerHTML = "";
54815         this.el.remove();
54816         this.el = null;
54817     },
54818     
54819     /**
54820      * form - if the content panel contains a form - this is a reference to it.
54821      * @type {Roo.form.Form}
54822      */
54823     form : false,
54824     /**
54825      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54826      *    This contains a reference to it.
54827      * @type {Roo.View}
54828      */
54829     view : false,
54830     
54831       /**
54832      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54833      * <pre><code>
54834
54835 layout.addxtype({
54836        xtype : 'Form',
54837        items: [ .... ]
54838    }
54839 );
54840
54841 </code></pre>
54842      * @param {Object} cfg Xtype definition of item to add.
54843      */
54844     
54845     addxtype : function(cfg) {
54846         // add form..
54847         if (cfg.xtype.match(/^Form$/)) {
54848             
54849             var el;
54850             //if (this.footer) {
54851             //    el = this.footer.container.insertSibling(false, 'before');
54852             //} else {
54853                 el = this.el.createChild();
54854             //}
54855
54856             this.form = new  Roo.form.Form(cfg);
54857             
54858             
54859             if ( this.form.allItems.length) {
54860                 this.form.render(el.dom);
54861             }
54862             return this.form;
54863         }
54864         // should only have one of theses..
54865         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54866             // views.. should not be just added - used named prop 'view''
54867             
54868             cfg.el = this.el.appendChild(document.createElement("div"));
54869             // factory?
54870             
54871             var ret = new Roo.factory(cfg);
54872              
54873              ret.render && ret.render(false, ''); // render blank..
54874             this.view = ret;
54875             return ret;
54876         }
54877         return false;
54878     }
54879 });
54880
54881 /**
54882  * @class Roo.GridPanel
54883  * @extends Roo.ContentPanel
54884  * @constructor
54885  * Create a new GridPanel.
54886  * @param {Roo.grid.Grid} grid The grid for this panel
54887  * @param {String/Object} config A string to set only the panel's title, or a config object
54888  */
54889 Roo.GridPanel = function(grid, config){
54890     
54891   
54892     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54893         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54894         
54895     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54896     
54897     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54898     
54899     if(this.toolbar){
54900         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54901     }
54902     // xtype created footer. - not sure if will work as we normally have to render first..
54903     if (this.footer && !this.footer.el && this.footer.xtype) {
54904         
54905         this.footer.container = this.grid.getView().getFooterPanel(true);
54906         this.footer.dataSource = this.grid.dataSource;
54907         this.footer = Roo.factory(this.footer, Roo);
54908         
54909     }
54910     
54911     grid.monitorWindowResize = false; // turn off autosizing
54912     grid.autoHeight = false;
54913     grid.autoWidth = false;
54914     this.grid = grid;
54915     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54916 };
54917
54918 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54919     getId : function(){
54920         return this.grid.id;
54921     },
54922     
54923     /**
54924      * Returns the grid for this panel
54925      * @return {Roo.grid.Grid} 
54926      */
54927     getGrid : function(){
54928         return this.grid;    
54929     },
54930     
54931     setSize : function(width, height){
54932         if(!this.ignoreResize(width, height)){
54933             var grid = this.grid;
54934             var size = this.adjustForComponents(width, height);
54935             grid.getGridEl().setSize(size.width, size.height);
54936             grid.autoSize();
54937         }
54938     },
54939     
54940     beforeSlide : function(){
54941         this.grid.getView().scroller.clip();
54942     },
54943     
54944     afterSlide : function(){
54945         this.grid.getView().scroller.unclip();
54946     },
54947     
54948     destroy : function(){
54949         this.grid.destroy();
54950         delete this.grid;
54951         Roo.GridPanel.superclass.destroy.call(this); 
54952     }
54953 });
54954
54955
54956 /**
54957  * @class Roo.NestedLayoutPanel
54958  * @extends Roo.ContentPanel
54959  * @constructor
54960  * Create a new NestedLayoutPanel.
54961  * 
54962  * 
54963  * @param {Roo.BorderLayout} layout [required] The layout for this panel
54964  * @param {String/Object} config A string to set only the title or a config object
54965  */
54966 Roo.NestedLayoutPanel = function(layout, config)
54967 {
54968     // construct with only one argument..
54969     /* FIXME - implement nicer consturctors
54970     if (layout.layout) {
54971         config = layout;
54972         layout = config.layout;
54973         delete config.layout;
54974     }
54975     if (layout.xtype && !layout.getEl) {
54976         // then layout needs constructing..
54977         layout = Roo.factory(layout, Roo);
54978     }
54979     */
54980     
54981     
54982     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54983     
54984     layout.monitorWindowResize = false; // turn off autosizing
54985     this.layout = layout;
54986     this.layout.getEl().addClass("x-layout-nested-layout");
54987     
54988     
54989     
54990     
54991 };
54992
54993 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54994
54995     setSize : function(width, height){
54996         if(!this.ignoreResize(width, height)){
54997             var size = this.adjustForComponents(width, height);
54998             var el = this.layout.getEl();
54999             el.setSize(size.width, size.height);
55000             var touch = el.dom.offsetWidth;
55001             this.layout.layout();
55002             // ie requires a double layout on the first pass
55003             if(Roo.isIE && !this.initialized){
55004                 this.initialized = true;
55005                 this.layout.layout();
55006             }
55007         }
55008     },
55009     
55010     // activate all subpanels if not currently active..
55011     
55012     setActiveState : function(active){
55013         this.active = active;
55014         if(!active){
55015             this.fireEvent("deactivate", this);
55016             return;
55017         }
55018         
55019         this.fireEvent("activate", this);
55020         // not sure if this should happen before or after..
55021         if (!this.layout) {
55022             return; // should not happen..
55023         }
55024         var reg = false;
55025         for (var r in this.layout.regions) {
55026             reg = this.layout.getRegion(r);
55027             if (reg.getActivePanel()) {
55028                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55029                 reg.setActivePanel(reg.getActivePanel());
55030                 continue;
55031             }
55032             if (!reg.panels.length) {
55033                 continue;
55034             }
55035             reg.showPanel(reg.getPanel(0));
55036         }
55037         
55038         
55039         
55040         
55041     },
55042     
55043     /**
55044      * Returns the nested BorderLayout for this panel
55045      * @return {Roo.BorderLayout} 
55046      */
55047     getLayout : function(){
55048         return this.layout;
55049     },
55050     
55051      /**
55052      * Adds a xtype elements to the layout of the nested panel
55053      * <pre><code>
55054
55055 panel.addxtype({
55056        xtype : 'ContentPanel',
55057        region: 'west',
55058        items: [ .... ]
55059    }
55060 );
55061
55062 panel.addxtype({
55063         xtype : 'NestedLayoutPanel',
55064         region: 'west',
55065         layout: {
55066            center: { },
55067            west: { }   
55068         },
55069         items : [ ... list of content panels or nested layout panels.. ]
55070    }
55071 );
55072 </code></pre>
55073      * @param {Object} cfg Xtype definition of item to add.
55074      */
55075     addxtype : function(cfg) {
55076         return this.layout.addxtype(cfg);
55077     
55078     }
55079 });
55080
55081 Roo.ScrollPanel = function(el, config, content){
55082     config = config || {};
55083     config.fitToFrame = true;
55084     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55085     
55086     this.el.dom.style.overflow = "hidden";
55087     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55088     this.el.removeClass("x-layout-inactive-content");
55089     this.el.on("mousewheel", this.onWheel, this);
55090
55091     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55092     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55093     up.unselectable(); down.unselectable();
55094     up.on("click", this.scrollUp, this);
55095     down.on("click", this.scrollDown, this);
55096     up.addClassOnOver("x-scroller-btn-over");
55097     down.addClassOnOver("x-scroller-btn-over");
55098     up.addClassOnClick("x-scroller-btn-click");
55099     down.addClassOnClick("x-scroller-btn-click");
55100     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55101
55102     this.resizeEl = this.el;
55103     this.el = wrap; this.up = up; this.down = down;
55104 };
55105
55106 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55107     increment : 100,
55108     wheelIncrement : 5,
55109     scrollUp : function(){
55110         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55111     },
55112
55113     scrollDown : function(){
55114         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55115     },
55116
55117     afterScroll : function(){
55118         var el = this.resizeEl;
55119         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55120         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55121         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55122     },
55123
55124     setSize : function(){
55125         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55126         this.afterScroll();
55127     },
55128
55129     onWheel : function(e){
55130         var d = e.getWheelDelta();
55131         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55132         this.afterScroll();
55133         e.stopEvent();
55134     },
55135
55136     setContent : function(content, loadScripts){
55137         this.resizeEl.update(content, loadScripts);
55138     }
55139
55140 });
55141
55142
55143
55144 /**
55145  * @class Roo.TreePanel
55146  * @extends Roo.ContentPanel
55147  * Treepanel component
55148  * 
55149  * @constructor
55150  * Create a new TreePanel. - defaults to fit/scoll contents.
55151  * @param {String/Object} config A string to set only the panel's title, or a config object
55152  */
55153 Roo.TreePanel = function(config){
55154     var el = config.el;
55155     var tree = config.tree;
55156     delete config.tree; 
55157     delete config.el; // hopefull!
55158     
55159     // wrapper for IE7 strict & safari scroll issue
55160     
55161     var treeEl = el.createChild();
55162     config.resizeEl = treeEl;
55163     
55164     
55165     
55166     Roo.TreePanel.superclass.constructor.call(this, el, config);
55167  
55168  
55169     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55170     //console.log(tree);
55171     this.on('activate', function()
55172     {
55173         if (this.tree.rendered) {
55174             return;
55175         }
55176         //console.log('render tree');
55177         this.tree.render();
55178     });
55179     // this should not be needed.. - it's actually the 'el' that resizes?
55180     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55181     
55182     //this.on('resize',  function (cp, w, h) {
55183     //        this.tree.innerCt.setWidth(w);
55184     //        this.tree.innerCt.setHeight(h);
55185     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55186     //});
55187
55188         
55189     
55190 };
55191
55192 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55193     fitToFrame : true,
55194     autoScroll : true,
55195     /*
55196      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55197      */
55198     tree : false
55199
55200 });
55201
55202
55203
55204
55205
55206
55207
55208
55209
55210
55211
55212 /*
55213  * Based on:
55214  * Ext JS Library 1.1.1
55215  * Copyright(c) 2006-2007, Ext JS, LLC.
55216  *
55217  * Originally Released Under LGPL - original licence link has changed is not relivant.
55218  *
55219  * Fork - LGPL
55220  * <script type="text/javascript">
55221  */
55222  
55223
55224 /**
55225  * @class Roo.ReaderLayout
55226  * @extends Roo.BorderLayout
55227  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55228  * center region containing two nested regions (a top one for a list view and one for item preview below),
55229  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55230  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55231  * expedites the setup of the overall layout and regions for this common application style.
55232  * Example:
55233  <pre><code>
55234 var reader = new Roo.ReaderLayout();
55235 var CP = Roo.ContentPanel;  // shortcut for adding
55236
55237 reader.beginUpdate();
55238 reader.add("north", new CP("north", "North"));
55239 reader.add("west", new CP("west", {title: "West"}));
55240 reader.add("east", new CP("east", {title: "East"}));
55241
55242 reader.regions.listView.add(new CP("listView", "List"));
55243 reader.regions.preview.add(new CP("preview", "Preview"));
55244 reader.endUpdate();
55245 </code></pre>
55246 * @constructor
55247 * Create a new ReaderLayout
55248 * @param {Object} config Configuration options
55249 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55250 * document.body if omitted)
55251 */
55252 Roo.ReaderLayout = function(config, renderTo){
55253     var c = config || {size:{}};
55254     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55255         north: c.north !== false ? Roo.apply({
55256             split:false,
55257             initialSize: 32,
55258             titlebar: false
55259         }, c.north) : false,
55260         west: c.west !== false ? Roo.apply({
55261             split:true,
55262             initialSize: 200,
55263             minSize: 175,
55264             maxSize: 400,
55265             titlebar: true,
55266             collapsible: true,
55267             animate: true,
55268             margins:{left:5,right:0,bottom:5,top:5},
55269             cmargins:{left:5,right:5,bottom:5,top:5}
55270         }, c.west) : false,
55271         east: c.east !== false ? Roo.apply({
55272             split:true,
55273             initialSize: 200,
55274             minSize: 175,
55275             maxSize: 400,
55276             titlebar: true,
55277             collapsible: true,
55278             animate: true,
55279             margins:{left:0,right:5,bottom:5,top:5},
55280             cmargins:{left:5,right:5,bottom:5,top:5}
55281         }, c.east) : false,
55282         center: Roo.apply({
55283             tabPosition: 'top',
55284             autoScroll:false,
55285             closeOnTab: true,
55286             titlebar:false,
55287             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55288         }, c.center)
55289     });
55290
55291     this.el.addClass('x-reader');
55292
55293     this.beginUpdate();
55294
55295     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55296         south: c.preview !== false ? Roo.apply({
55297             split:true,
55298             initialSize: 200,
55299             minSize: 100,
55300             autoScroll:true,
55301             collapsible:true,
55302             titlebar: true,
55303             cmargins:{top:5,left:0, right:0, bottom:0}
55304         }, c.preview) : false,
55305         center: Roo.apply({
55306             autoScroll:false,
55307             titlebar:false,
55308             minHeight:200
55309         }, c.listView)
55310     });
55311     this.add('center', new Roo.NestedLayoutPanel(inner,
55312             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55313
55314     this.endUpdate();
55315
55316     this.regions.preview = inner.getRegion('south');
55317     this.regions.listView = inner.getRegion('center');
55318 };
55319
55320 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55321  * Based on:
55322  * Ext JS Library 1.1.1
55323  * Copyright(c) 2006-2007, Ext JS, LLC.
55324  *
55325  * Originally Released Under LGPL - original licence link has changed is not relivant.
55326  *
55327  * Fork - LGPL
55328  * <script type="text/javascript">
55329  */
55330  
55331 /**
55332  * @class Roo.grid.Grid
55333  * @extends Roo.util.Observable
55334  * This class represents the primary interface of a component based grid control.
55335  * <br><br>Usage:<pre><code>
55336  var grid = new Roo.grid.Grid("my-container-id", {
55337      ds: myDataStore,
55338      cm: myColModel,
55339      selModel: mySelectionModel,
55340      autoSizeColumns: true,
55341      monitorWindowResize: false,
55342      trackMouseOver: true
55343  });
55344  // set any options
55345  grid.render();
55346  * </code></pre>
55347  * <b>Common Problems:</b><br/>
55348  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55349  * element will correct this<br/>
55350  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55351  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55352  * are unpredictable.<br/>
55353  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55354  * grid to calculate dimensions/offsets.<br/>
55355   * @constructor
55356  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55357  * The container MUST have some type of size defined for the grid to fill. The container will be
55358  * automatically set to position relative if it isn't already.
55359  * @param {Object} config A config object that sets properties on this grid.
55360  */
55361 Roo.grid.Grid = function(container, config){
55362         // initialize the container
55363         this.container = Roo.get(container);
55364         this.container.update("");
55365         this.container.setStyle("overflow", "hidden");
55366     this.container.addClass('x-grid-container');
55367
55368     this.id = this.container.id;
55369
55370     Roo.apply(this, config);
55371     // check and correct shorthanded configs
55372     if(this.ds){
55373         this.dataSource = this.ds;
55374         delete this.ds;
55375     }
55376     if(this.cm){
55377         this.colModel = this.cm;
55378         delete this.cm;
55379     }
55380     if(this.sm){
55381         this.selModel = this.sm;
55382         delete this.sm;
55383     }
55384
55385     if (this.selModel) {
55386         this.selModel = Roo.factory(this.selModel, Roo.grid);
55387         this.sm = this.selModel;
55388         this.sm.xmodule = this.xmodule || false;
55389     }
55390     if (typeof(this.colModel.config) == 'undefined') {
55391         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55392         this.cm = this.colModel;
55393         this.cm.xmodule = this.xmodule || false;
55394     }
55395     if (this.dataSource) {
55396         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55397         this.ds = this.dataSource;
55398         this.ds.xmodule = this.xmodule || false;
55399          
55400     }
55401     
55402     
55403     
55404     if(this.width){
55405         this.container.setWidth(this.width);
55406     }
55407
55408     if(this.height){
55409         this.container.setHeight(this.height);
55410     }
55411     /** @private */
55412         this.addEvents({
55413         // raw events
55414         /**
55415          * @event click
55416          * The raw click event for the entire grid.
55417          * @param {Roo.EventObject} e
55418          */
55419         "click" : true,
55420         /**
55421          * @event dblclick
55422          * The raw dblclick event for the entire grid.
55423          * @param {Roo.EventObject} e
55424          */
55425         "dblclick" : true,
55426         /**
55427          * @event contextmenu
55428          * The raw contextmenu event for the entire grid.
55429          * @param {Roo.EventObject} e
55430          */
55431         "contextmenu" : true,
55432         /**
55433          * @event mousedown
55434          * The raw mousedown event for the entire grid.
55435          * @param {Roo.EventObject} e
55436          */
55437         "mousedown" : true,
55438         /**
55439          * @event mouseup
55440          * The raw mouseup event for the entire grid.
55441          * @param {Roo.EventObject} e
55442          */
55443         "mouseup" : true,
55444         /**
55445          * @event mouseover
55446          * The raw mouseover event for the entire grid.
55447          * @param {Roo.EventObject} e
55448          */
55449         "mouseover" : true,
55450         /**
55451          * @event mouseout
55452          * The raw mouseout event for the entire grid.
55453          * @param {Roo.EventObject} e
55454          */
55455         "mouseout" : true,
55456         /**
55457          * @event keypress
55458          * The raw keypress event for the entire grid.
55459          * @param {Roo.EventObject} e
55460          */
55461         "keypress" : true,
55462         /**
55463          * @event keydown
55464          * The raw keydown event for the entire grid.
55465          * @param {Roo.EventObject} e
55466          */
55467         "keydown" : true,
55468
55469         // custom events
55470
55471         /**
55472          * @event cellclick
55473          * Fires when a cell is clicked
55474          * @param {Grid} this
55475          * @param {Number} rowIndex
55476          * @param {Number} columnIndex
55477          * @param {Roo.EventObject} e
55478          */
55479         "cellclick" : true,
55480         /**
55481          * @event celldblclick
55482          * Fires when a cell is double clicked
55483          * @param {Grid} this
55484          * @param {Number} rowIndex
55485          * @param {Number} columnIndex
55486          * @param {Roo.EventObject} e
55487          */
55488         "celldblclick" : true,
55489         /**
55490          * @event rowclick
55491          * Fires when a row is clicked
55492          * @param {Grid} this
55493          * @param {Number} rowIndex
55494          * @param {Roo.EventObject} e
55495          */
55496         "rowclick" : true,
55497         /**
55498          * @event rowdblclick
55499          * Fires when a row is double clicked
55500          * @param {Grid} this
55501          * @param {Number} rowIndex
55502          * @param {Roo.EventObject} e
55503          */
55504         "rowdblclick" : true,
55505         /**
55506          * @event headerclick
55507          * Fires when a header is clicked
55508          * @param {Grid} this
55509          * @param {Number} columnIndex
55510          * @param {Roo.EventObject} e
55511          */
55512         "headerclick" : true,
55513         /**
55514          * @event headerdblclick
55515          * Fires when a header cell is double clicked
55516          * @param {Grid} this
55517          * @param {Number} columnIndex
55518          * @param {Roo.EventObject} e
55519          */
55520         "headerdblclick" : true,
55521         /**
55522          * @event rowcontextmenu
55523          * Fires when a row is right clicked
55524          * @param {Grid} this
55525          * @param {Number} rowIndex
55526          * @param {Roo.EventObject} e
55527          */
55528         "rowcontextmenu" : true,
55529         /**
55530          * @event cellcontextmenu
55531          * Fires when a cell is right clicked
55532          * @param {Grid} this
55533          * @param {Number} rowIndex
55534          * @param {Number} cellIndex
55535          * @param {Roo.EventObject} e
55536          */
55537          "cellcontextmenu" : true,
55538         /**
55539          * @event headercontextmenu
55540          * Fires when a header is right clicked
55541          * @param {Grid} this
55542          * @param {Number} columnIndex
55543          * @param {Roo.EventObject} e
55544          */
55545         "headercontextmenu" : true,
55546         /**
55547          * @event bodyscroll
55548          * Fires when the body element is scrolled
55549          * @param {Number} scrollLeft
55550          * @param {Number} scrollTop
55551          */
55552         "bodyscroll" : true,
55553         /**
55554          * @event columnresize
55555          * Fires when the user resizes a column
55556          * @param {Number} columnIndex
55557          * @param {Number} newSize
55558          */
55559         "columnresize" : true,
55560         /**
55561          * @event columnmove
55562          * Fires when the user moves a column
55563          * @param {Number} oldIndex
55564          * @param {Number} newIndex
55565          */
55566         "columnmove" : true,
55567         /**
55568          * @event startdrag
55569          * Fires when row(s) start being dragged
55570          * @param {Grid} this
55571          * @param {Roo.GridDD} dd The drag drop object
55572          * @param {event} e The raw browser event
55573          */
55574         "startdrag" : true,
55575         /**
55576          * @event enddrag
55577          * Fires when a drag operation is complete
55578          * @param {Grid} this
55579          * @param {Roo.GridDD} dd The drag drop object
55580          * @param {event} e The raw browser event
55581          */
55582         "enddrag" : true,
55583         /**
55584          * @event dragdrop
55585          * Fires when dragged row(s) are dropped on a valid DD target
55586          * @param {Grid} this
55587          * @param {Roo.GridDD} dd The drag drop object
55588          * @param {String} targetId The target drag drop object
55589          * @param {event} e The raw browser event
55590          */
55591         "dragdrop" : true,
55592         /**
55593          * @event dragover
55594          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55595          * @param {Grid} this
55596          * @param {Roo.GridDD} dd The drag drop object
55597          * @param {String} targetId The target drag drop object
55598          * @param {event} e The raw browser event
55599          */
55600         "dragover" : true,
55601         /**
55602          * @event dragenter
55603          *  Fires when the dragged row(s) first cross another DD target while being dragged
55604          * @param {Grid} this
55605          * @param {Roo.GridDD} dd The drag drop object
55606          * @param {String} targetId The target drag drop object
55607          * @param {event} e The raw browser event
55608          */
55609         "dragenter" : true,
55610         /**
55611          * @event dragout
55612          * Fires when the dragged row(s) leave another DD target while being dragged
55613          * @param {Grid} this
55614          * @param {Roo.GridDD} dd The drag drop object
55615          * @param {String} targetId The target drag drop object
55616          * @param {event} e The raw browser event
55617          */
55618         "dragout" : true,
55619         /**
55620          * @event rowclass
55621          * Fires when a row is rendered, so you can change add a style to it.
55622          * @param {GridView} gridview   The grid view
55623          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55624          */
55625         'rowclass' : true,
55626
55627         /**
55628          * @event render
55629          * Fires when the grid is rendered
55630          * @param {Grid} grid
55631          */
55632         'render' : true
55633     });
55634
55635     Roo.grid.Grid.superclass.constructor.call(this);
55636 };
55637 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55638     
55639     /**
55640          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
55641          */
55642         /**
55643          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
55644          */
55645         /**
55646          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
55647          */
55648         /**
55649          * @cfg {Roo.grid.Store} ds The data store for the grid
55650          */
55651         /**
55652          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
55653          */
55654         /**
55655      * @cfg {String} ddGroup - drag drop group.
55656      */
55657       /**
55658      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55659      */
55660
55661     /**
55662      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55663      */
55664     minColumnWidth : 25,
55665
55666     /**
55667      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55668      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55669      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55670      */
55671     autoSizeColumns : false,
55672
55673     /**
55674      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55675      */
55676     autoSizeHeaders : true,
55677
55678     /**
55679      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55680      */
55681     monitorWindowResize : true,
55682
55683     /**
55684      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55685      * rows measured to get a columns size. Default is 0 (all rows).
55686      */
55687     maxRowsToMeasure : 0,
55688
55689     /**
55690      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55691      */
55692     trackMouseOver : true,
55693
55694     /**
55695     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55696     */
55697       /**
55698     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55699     */
55700     
55701     /**
55702     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55703     */
55704     enableDragDrop : false,
55705     
55706     /**
55707     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55708     */
55709     enableColumnMove : true,
55710     
55711     /**
55712     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55713     */
55714     enableColumnHide : true,
55715     
55716     /**
55717     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55718     */
55719     enableRowHeightSync : false,
55720     
55721     /**
55722     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55723     */
55724     stripeRows : true,
55725     
55726     /**
55727     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55728     */
55729     autoHeight : false,
55730
55731     /**
55732      * @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.
55733      */
55734     autoExpandColumn : false,
55735
55736     /**
55737     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55738     * Default is 50.
55739     */
55740     autoExpandMin : 50,
55741
55742     /**
55743     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55744     */
55745     autoExpandMax : 1000,
55746
55747     /**
55748     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55749     */
55750     view : null,
55751
55752     /**
55753     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55754     */
55755     loadMask : false,
55756     /**
55757     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55758     */
55759     dropTarget: false,
55760     
55761    
55762     
55763     // private
55764     rendered : false,
55765
55766     /**
55767     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55768     * of a fixed width. Default is false.
55769     */
55770     /**
55771     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55772     */
55773     
55774     
55775     /**
55776     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55777     * %0 is replaced with the number of selected rows.
55778     */
55779     ddText : "{0} selected row{1}",
55780     
55781     
55782     /**
55783      * Called once after all setup has been completed and the grid is ready to be rendered.
55784      * @return {Roo.grid.Grid} this
55785      */
55786     render : function()
55787     {
55788         var c = this.container;
55789         // try to detect autoHeight/width mode
55790         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55791             this.autoHeight = true;
55792         }
55793         var view = this.getView();
55794         view.init(this);
55795
55796         c.on("click", this.onClick, this);
55797         c.on("dblclick", this.onDblClick, this);
55798         c.on("contextmenu", this.onContextMenu, this);
55799         c.on("keydown", this.onKeyDown, this);
55800         if (Roo.isTouch) {
55801             c.on("touchstart", this.onTouchStart, this);
55802         }
55803
55804         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55805
55806         this.getSelectionModel().init(this);
55807
55808         view.render();
55809
55810         if(this.loadMask){
55811             this.loadMask = new Roo.LoadMask(this.container,
55812                     Roo.apply({store:this.dataSource}, this.loadMask));
55813         }
55814         
55815         
55816         if (this.toolbar && this.toolbar.xtype) {
55817             this.toolbar.container = this.getView().getHeaderPanel(true);
55818             this.toolbar = new Roo.Toolbar(this.toolbar);
55819         }
55820         if (this.footer && this.footer.xtype) {
55821             this.footer.dataSource = this.getDataSource();
55822             this.footer.container = this.getView().getFooterPanel(true);
55823             this.footer = Roo.factory(this.footer, Roo);
55824         }
55825         if (this.dropTarget && this.dropTarget.xtype) {
55826             delete this.dropTarget.xtype;
55827             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55828         }
55829         
55830         
55831         this.rendered = true;
55832         this.fireEvent('render', this);
55833         return this;
55834     },
55835
55836     /**
55837      * Reconfigures the grid to use a different Store and Column Model.
55838      * The View will be bound to the new objects and refreshed.
55839      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55840      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55841      */
55842     reconfigure : function(dataSource, colModel){
55843         if(this.loadMask){
55844             this.loadMask.destroy();
55845             this.loadMask = new Roo.LoadMask(this.container,
55846                     Roo.apply({store:dataSource}, this.loadMask));
55847         }
55848         this.view.bind(dataSource, colModel);
55849         this.dataSource = dataSource;
55850         this.colModel = colModel;
55851         this.view.refresh(true);
55852     },
55853     /**
55854      * addColumns
55855      * Add's a column, default at the end..
55856      
55857      * @param {int} position to add (default end)
55858      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55859      */
55860     addColumns : function(pos, ar)
55861     {
55862         
55863         for (var i =0;i< ar.length;i++) {
55864             var cfg = ar[i];
55865             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55866             this.cm.lookup[cfg.id] = cfg;
55867         }
55868         
55869         
55870         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55871             pos = this.cm.config.length; //this.cm.config.push(cfg);
55872         } 
55873         pos = Math.max(0,pos);
55874         ar.unshift(0);
55875         ar.unshift(pos);
55876         this.cm.config.splice.apply(this.cm.config, ar);
55877         
55878         
55879         
55880         this.view.generateRules(this.cm);
55881         this.view.refresh(true);
55882         
55883     },
55884     
55885     
55886     
55887     
55888     // private
55889     onKeyDown : function(e){
55890         this.fireEvent("keydown", e);
55891     },
55892
55893     /**
55894      * Destroy this grid.
55895      * @param {Boolean} removeEl True to remove the element
55896      */
55897     destroy : function(removeEl, keepListeners){
55898         if(this.loadMask){
55899             this.loadMask.destroy();
55900         }
55901         var c = this.container;
55902         c.removeAllListeners();
55903         this.view.destroy();
55904         this.colModel.purgeListeners();
55905         if(!keepListeners){
55906             this.purgeListeners();
55907         }
55908         c.update("");
55909         if(removeEl === true){
55910             c.remove();
55911         }
55912     },
55913
55914     // private
55915     processEvent : function(name, e){
55916         // does this fire select???
55917         //Roo.log('grid:processEvent '  + name);
55918         
55919         if (name != 'touchstart' ) {
55920             this.fireEvent(name, e);    
55921         }
55922         
55923         var t = e.getTarget();
55924         var v = this.view;
55925         var header = v.findHeaderIndex(t);
55926         if(header !== false){
55927             var ename = name == 'touchstart' ? 'click' : name;
55928              
55929             this.fireEvent("header" + ename, this, header, e);
55930         }else{
55931             var row = v.findRowIndex(t);
55932             var cell = v.findCellIndex(t);
55933             if (name == 'touchstart') {
55934                 // first touch is always a click.
55935                 // hopefull this happens after selection is updated.?
55936                 name = false;
55937                 
55938                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55939                     var cs = this.selModel.getSelectedCell();
55940                     if (row == cs[0] && cell == cs[1]){
55941                         name = 'dblclick';
55942                     }
55943                 }
55944                 if (typeof(this.selModel.getSelections) != 'undefined') {
55945                     var cs = this.selModel.getSelections();
55946                     var ds = this.dataSource;
55947                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55948                         name = 'dblclick';
55949                     }
55950                 }
55951                 if (!name) {
55952                     return;
55953                 }
55954             }
55955             
55956             
55957             if(row !== false){
55958                 this.fireEvent("row" + name, this, row, e);
55959                 if(cell !== false){
55960                     this.fireEvent("cell" + name, this, row, cell, e);
55961                 }
55962             }
55963         }
55964     },
55965
55966     // private
55967     onClick : function(e){
55968         this.processEvent("click", e);
55969     },
55970    // private
55971     onTouchStart : function(e){
55972         this.processEvent("touchstart", e);
55973     },
55974
55975     // private
55976     onContextMenu : function(e, t){
55977         this.processEvent("contextmenu", e);
55978     },
55979
55980     // private
55981     onDblClick : function(e){
55982         this.processEvent("dblclick", e);
55983     },
55984
55985     // private
55986     walkCells : function(row, col, step, fn, scope){
55987         var cm = this.colModel, clen = cm.getColumnCount();
55988         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55989         if(step < 0){
55990             if(col < 0){
55991                 row--;
55992                 first = false;
55993             }
55994             while(row >= 0){
55995                 if(!first){
55996                     col = clen-1;
55997                 }
55998                 first = false;
55999                 while(col >= 0){
56000                     if(fn.call(scope || this, row, col, cm) === true){
56001                         return [row, col];
56002                     }
56003                     col--;
56004                 }
56005                 row--;
56006             }
56007         } else {
56008             if(col >= clen){
56009                 row++;
56010                 first = false;
56011             }
56012             while(row < rlen){
56013                 if(!first){
56014                     col = 0;
56015                 }
56016                 first = false;
56017                 while(col < clen){
56018                     if(fn.call(scope || this, row, col, cm) === true){
56019                         return [row, col];
56020                     }
56021                     col++;
56022                 }
56023                 row++;
56024             }
56025         }
56026         return null;
56027     },
56028
56029     // private
56030     getSelections : function(){
56031         return this.selModel.getSelections();
56032     },
56033
56034     /**
56035      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56036      * but if manual update is required this method will initiate it.
56037      */
56038     autoSize : function(){
56039         if(this.rendered){
56040             this.view.layout();
56041             if(this.view.adjustForScroll){
56042                 this.view.adjustForScroll();
56043             }
56044         }
56045     },
56046
56047     /**
56048      * Returns the grid's underlying element.
56049      * @return {Element} The element
56050      */
56051     getGridEl : function(){
56052         return this.container;
56053     },
56054
56055     // private for compatibility, overridden by editor grid
56056     stopEditing : function(){},
56057
56058     /**
56059      * Returns the grid's SelectionModel.
56060      * @return {SelectionModel}
56061      */
56062     getSelectionModel : function(){
56063         if(!this.selModel){
56064             this.selModel = new Roo.grid.RowSelectionModel();
56065         }
56066         return this.selModel;
56067     },
56068
56069     /**
56070      * Returns the grid's DataSource.
56071      * @return {DataSource}
56072      */
56073     getDataSource : function(){
56074         return this.dataSource;
56075     },
56076
56077     /**
56078      * Returns the grid's ColumnModel.
56079      * @return {ColumnModel}
56080      */
56081     getColumnModel : function(){
56082         return this.colModel;
56083     },
56084
56085     /**
56086      * Returns the grid's GridView object.
56087      * @return {GridView}
56088      */
56089     getView : function(){
56090         if(!this.view){
56091             this.view = new Roo.grid.GridView(this.viewConfig);
56092             this.relayEvents(this.view, [
56093                 "beforerowremoved", "beforerowsinserted",
56094                 "beforerefresh", "rowremoved",
56095                 "rowsinserted", "rowupdated" ,"refresh"
56096             ]);
56097         }
56098         return this.view;
56099     },
56100     /**
56101      * Called to get grid's drag proxy text, by default returns this.ddText.
56102      * Override this to put something different in the dragged text.
56103      * @return {String}
56104      */
56105     getDragDropText : function(){
56106         var count = this.selModel.getCount();
56107         return String.format(this.ddText, count, count == 1 ? '' : 's');
56108     }
56109 });
56110 /*
56111  * Based on:
56112  * Ext JS Library 1.1.1
56113  * Copyright(c) 2006-2007, Ext JS, LLC.
56114  *
56115  * Originally Released Under LGPL - original licence link has changed is not relivant.
56116  *
56117  * Fork - LGPL
56118  * <script type="text/javascript">
56119  */
56120  /**
56121  * @class Roo.grid.AbstractGridView
56122  * @extends Roo.util.Observable
56123  * @abstract
56124  * Abstract base class for grid Views
56125  * @constructor
56126  */
56127 Roo.grid.AbstractGridView = function(){
56128         this.grid = null;
56129         
56130         this.events = {
56131             "beforerowremoved" : true,
56132             "beforerowsinserted" : true,
56133             "beforerefresh" : true,
56134             "rowremoved" : true,
56135             "rowsinserted" : true,
56136             "rowupdated" : true,
56137             "refresh" : true
56138         };
56139     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56140 };
56141
56142 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56143     rowClass : "x-grid-row",
56144     cellClass : "x-grid-cell",
56145     tdClass : "x-grid-td",
56146     hdClass : "x-grid-hd",
56147     splitClass : "x-grid-hd-split",
56148     
56149     init: function(grid){
56150         this.grid = grid;
56151                 var cid = this.grid.getGridEl().id;
56152         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56153         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56154         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56155         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56156         },
56157         
56158     getColumnRenderers : function(){
56159         var renderers = [];
56160         var cm = this.grid.colModel;
56161         var colCount = cm.getColumnCount();
56162         for(var i = 0; i < colCount; i++){
56163             renderers[i] = cm.getRenderer(i);
56164         }
56165         return renderers;
56166     },
56167     
56168     getColumnIds : function(){
56169         var ids = [];
56170         var cm = this.grid.colModel;
56171         var colCount = cm.getColumnCount();
56172         for(var i = 0; i < colCount; i++){
56173             ids[i] = cm.getColumnId(i);
56174         }
56175         return ids;
56176     },
56177     
56178     getDataIndexes : function(){
56179         if(!this.indexMap){
56180             this.indexMap = this.buildIndexMap();
56181         }
56182         return this.indexMap.colToData;
56183     },
56184     
56185     getColumnIndexByDataIndex : function(dataIndex){
56186         if(!this.indexMap){
56187             this.indexMap = this.buildIndexMap();
56188         }
56189         return this.indexMap.dataToCol[dataIndex];
56190     },
56191     
56192     /**
56193      * Set a css style for a column dynamically. 
56194      * @param {Number} colIndex The index of the column
56195      * @param {String} name The css property name
56196      * @param {String} value The css value
56197      */
56198     setCSSStyle : function(colIndex, name, value){
56199         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56200         Roo.util.CSS.updateRule(selector, name, value);
56201     },
56202     
56203     generateRules : function(cm){
56204         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56205         Roo.util.CSS.removeStyleSheet(rulesId);
56206         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56207             var cid = cm.getColumnId(i);
56208             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56209                          this.tdSelector, cid, " {\n}\n",
56210                          this.hdSelector, cid, " {\n}\n",
56211                          this.splitSelector, cid, " {\n}\n");
56212         }
56213         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56214     }
56215 });/*
56216  * Based on:
56217  * Ext JS Library 1.1.1
56218  * Copyright(c) 2006-2007, Ext JS, LLC.
56219  *
56220  * Originally Released Under LGPL - original licence link has changed is not relivant.
56221  *
56222  * Fork - LGPL
56223  * <script type="text/javascript">
56224  */
56225
56226 // private
56227 // This is a support class used internally by the Grid components
56228 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56229     this.grid = grid;
56230     this.view = grid.getView();
56231     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56232     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56233     if(hd2){
56234         this.setHandleElId(Roo.id(hd));
56235         this.setOuterHandleElId(Roo.id(hd2));
56236     }
56237     this.scroll = false;
56238 };
56239 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56240     maxDragWidth: 120,
56241     getDragData : function(e){
56242         var t = Roo.lib.Event.getTarget(e);
56243         var h = this.view.findHeaderCell(t);
56244         if(h){
56245             return {ddel: h.firstChild, header:h};
56246         }
56247         return false;
56248     },
56249
56250     onInitDrag : function(e){
56251         this.view.headersDisabled = true;
56252         var clone = this.dragData.ddel.cloneNode(true);
56253         clone.id = Roo.id();
56254         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56255         this.proxy.update(clone);
56256         return true;
56257     },
56258
56259     afterValidDrop : function(){
56260         var v = this.view;
56261         setTimeout(function(){
56262             v.headersDisabled = false;
56263         }, 50);
56264     },
56265
56266     afterInvalidDrop : function(){
56267         var v = this.view;
56268         setTimeout(function(){
56269             v.headersDisabled = false;
56270         }, 50);
56271     }
56272 });
56273 /*
56274  * Based on:
56275  * Ext JS Library 1.1.1
56276  * Copyright(c) 2006-2007, Ext JS, LLC.
56277  *
56278  * Originally Released Under LGPL - original licence link has changed is not relivant.
56279  *
56280  * Fork - LGPL
56281  * <script type="text/javascript">
56282  */
56283 // private
56284 // This is a support class used internally by the Grid components
56285 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56286     this.grid = grid;
56287     this.view = grid.getView();
56288     // split the proxies so they don't interfere with mouse events
56289     this.proxyTop = Roo.DomHelper.append(document.body, {
56290         cls:"col-move-top", html:"&#160;"
56291     }, true);
56292     this.proxyBottom = Roo.DomHelper.append(document.body, {
56293         cls:"col-move-bottom", html:"&#160;"
56294     }, true);
56295     this.proxyTop.hide = this.proxyBottom.hide = function(){
56296         this.setLeftTop(-100,-100);
56297         this.setStyle("visibility", "hidden");
56298     };
56299     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56300     // temporarily disabled
56301     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56302     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56303 };
56304 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56305     proxyOffsets : [-4, -9],
56306     fly: Roo.Element.fly,
56307
56308     getTargetFromEvent : function(e){
56309         var t = Roo.lib.Event.getTarget(e);
56310         var cindex = this.view.findCellIndex(t);
56311         if(cindex !== false){
56312             return this.view.getHeaderCell(cindex);
56313         }
56314         return null;
56315     },
56316
56317     nextVisible : function(h){
56318         var v = this.view, cm = this.grid.colModel;
56319         h = h.nextSibling;
56320         while(h){
56321             if(!cm.isHidden(v.getCellIndex(h))){
56322                 return h;
56323             }
56324             h = h.nextSibling;
56325         }
56326         return null;
56327     },
56328
56329     prevVisible : function(h){
56330         var v = this.view, cm = this.grid.colModel;
56331         h = h.prevSibling;
56332         while(h){
56333             if(!cm.isHidden(v.getCellIndex(h))){
56334                 return h;
56335             }
56336             h = h.prevSibling;
56337         }
56338         return null;
56339     },
56340
56341     positionIndicator : function(h, n, e){
56342         var x = Roo.lib.Event.getPageX(e);
56343         var r = Roo.lib.Dom.getRegion(n.firstChild);
56344         var px, pt, py = r.top + this.proxyOffsets[1];
56345         if((r.right - x) <= (r.right-r.left)/2){
56346             px = r.right+this.view.borderWidth;
56347             pt = "after";
56348         }else{
56349             px = r.left;
56350             pt = "before";
56351         }
56352         var oldIndex = this.view.getCellIndex(h);
56353         var newIndex = this.view.getCellIndex(n);
56354
56355         if(this.grid.colModel.isFixed(newIndex)){
56356             return false;
56357         }
56358
56359         var locked = this.grid.colModel.isLocked(newIndex);
56360
56361         if(pt == "after"){
56362             newIndex++;
56363         }
56364         if(oldIndex < newIndex){
56365             newIndex--;
56366         }
56367         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56368             return false;
56369         }
56370         px +=  this.proxyOffsets[0];
56371         this.proxyTop.setLeftTop(px, py);
56372         this.proxyTop.show();
56373         if(!this.bottomOffset){
56374             this.bottomOffset = this.view.mainHd.getHeight();
56375         }
56376         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56377         this.proxyBottom.show();
56378         return pt;
56379     },
56380
56381     onNodeEnter : function(n, dd, e, data){
56382         if(data.header != n){
56383             this.positionIndicator(data.header, n, e);
56384         }
56385     },
56386
56387     onNodeOver : function(n, dd, e, data){
56388         var result = false;
56389         if(data.header != n){
56390             result = this.positionIndicator(data.header, n, e);
56391         }
56392         if(!result){
56393             this.proxyTop.hide();
56394             this.proxyBottom.hide();
56395         }
56396         return result ? this.dropAllowed : this.dropNotAllowed;
56397     },
56398
56399     onNodeOut : function(n, dd, e, data){
56400         this.proxyTop.hide();
56401         this.proxyBottom.hide();
56402     },
56403
56404     onNodeDrop : function(n, dd, e, data){
56405         var h = data.header;
56406         if(h != n){
56407             var cm = this.grid.colModel;
56408             var x = Roo.lib.Event.getPageX(e);
56409             var r = Roo.lib.Dom.getRegion(n.firstChild);
56410             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56411             var oldIndex = this.view.getCellIndex(h);
56412             var newIndex = this.view.getCellIndex(n);
56413             var locked = cm.isLocked(newIndex);
56414             if(pt == "after"){
56415                 newIndex++;
56416             }
56417             if(oldIndex < newIndex){
56418                 newIndex--;
56419             }
56420             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56421                 return false;
56422             }
56423             cm.setLocked(oldIndex, locked, true);
56424             cm.moveColumn(oldIndex, newIndex);
56425             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56426             return true;
56427         }
56428         return false;
56429     }
56430 });
56431 /*
56432  * Based on:
56433  * Ext JS Library 1.1.1
56434  * Copyright(c) 2006-2007, Ext JS, LLC.
56435  *
56436  * Originally Released Under LGPL - original licence link has changed is not relivant.
56437  *
56438  * Fork - LGPL
56439  * <script type="text/javascript">
56440  */
56441   
56442 /**
56443  * @class Roo.grid.GridView
56444  * @extends Roo.util.Observable
56445  *
56446  * @constructor
56447  * @param {Object} config
56448  */
56449 Roo.grid.GridView = function(config){
56450     Roo.grid.GridView.superclass.constructor.call(this);
56451     this.el = null;
56452
56453     Roo.apply(this, config);
56454 };
56455
56456 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56457
56458     unselectable :  'unselectable="on"',
56459     unselectableCls :  'x-unselectable',
56460     
56461     
56462     rowClass : "x-grid-row",
56463
56464     cellClass : "x-grid-col",
56465
56466     tdClass : "x-grid-td",
56467
56468     hdClass : "x-grid-hd",
56469
56470     splitClass : "x-grid-split",
56471
56472     sortClasses : ["sort-asc", "sort-desc"],
56473
56474     enableMoveAnim : false,
56475
56476     hlColor: "C3DAF9",
56477
56478     dh : Roo.DomHelper,
56479
56480     fly : Roo.Element.fly,
56481
56482     css : Roo.util.CSS,
56483
56484     borderWidth: 1,
56485
56486     splitOffset: 3,
56487
56488     scrollIncrement : 22,
56489
56490     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56491
56492     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56493
56494     bind : function(ds, cm){
56495         if(this.ds){
56496             this.ds.un("load", this.onLoad, this);
56497             this.ds.un("datachanged", this.onDataChange, this);
56498             this.ds.un("add", this.onAdd, this);
56499             this.ds.un("remove", this.onRemove, this);
56500             this.ds.un("update", this.onUpdate, this);
56501             this.ds.un("clear", this.onClear, this);
56502         }
56503         if(ds){
56504             ds.on("load", this.onLoad, this);
56505             ds.on("datachanged", this.onDataChange, this);
56506             ds.on("add", this.onAdd, this);
56507             ds.on("remove", this.onRemove, this);
56508             ds.on("update", this.onUpdate, this);
56509             ds.on("clear", this.onClear, this);
56510         }
56511         this.ds = ds;
56512
56513         if(this.cm){
56514             this.cm.un("widthchange", this.onColWidthChange, this);
56515             this.cm.un("headerchange", this.onHeaderChange, this);
56516             this.cm.un("hiddenchange", this.onHiddenChange, this);
56517             this.cm.un("columnmoved", this.onColumnMove, this);
56518             this.cm.un("columnlockchange", this.onColumnLock, this);
56519         }
56520         if(cm){
56521             this.generateRules(cm);
56522             cm.on("widthchange", this.onColWidthChange, this);
56523             cm.on("headerchange", this.onHeaderChange, this);
56524             cm.on("hiddenchange", this.onHiddenChange, this);
56525             cm.on("columnmoved", this.onColumnMove, this);
56526             cm.on("columnlockchange", this.onColumnLock, this);
56527         }
56528         this.cm = cm;
56529     },
56530
56531     init: function(grid){
56532         Roo.grid.GridView.superclass.init.call(this, grid);
56533
56534         this.bind(grid.dataSource, grid.colModel);
56535
56536         grid.on("headerclick", this.handleHeaderClick, this);
56537
56538         if(grid.trackMouseOver){
56539             grid.on("mouseover", this.onRowOver, this);
56540             grid.on("mouseout", this.onRowOut, this);
56541         }
56542         grid.cancelTextSelection = function(){};
56543         this.gridId = grid.id;
56544
56545         var tpls = this.templates || {};
56546
56547         if(!tpls.master){
56548             tpls.master = new Roo.Template(
56549                '<div class="x-grid" hidefocus="true">',
56550                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56551                   '<div class="x-grid-topbar"></div>',
56552                   '<div class="x-grid-scroller"><div></div></div>',
56553                   '<div class="x-grid-locked">',
56554                       '<div class="x-grid-header">{lockedHeader}</div>',
56555                       '<div class="x-grid-body">{lockedBody}</div>',
56556                   "</div>",
56557                   '<div class="x-grid-viewport">',
56558                       '<div class="x-grid-header">{header}</div>',
56559                       '<div class="x-grid-body">{body}</div>',
56560                   "</div>",
56561                   '<div class="x-grid-bottombar"></div>',
56562                  
56563                   '<div class="x-grid-resize-proxy">&#160;</div>',
56564                "</div>"
56565             );
56566             tpls.master.disableformats = true;
56567         }
56568
56569         if(!tpls.header){
56570             tpls.header = new Roo.Template(
56571                '<table border="0" cellspacing="0" cellpadding="0">',
56572                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56573                "</table>{splits}"
56574             );
56575             tpls.header.disableformats = true;
56576         }
56577         tpls.header.compile();
56578
56579         if(!tpls.hcell){
56580             tpls.hcell = new Roo.Template(
56581                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56582                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56583                 "</div></td>"
56584              );
56585              tpls.hcell.disableFormats = true;
56586         }
56587         tpls.hcell.compile();
56588
56589         if(!tpls.hsplit){
56590             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56591                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56592             tpls.hsplit.disableFormats = true;
56593         }
56594         tpls.hsplit.compile();
56595
56596         if(!tpls.body){
56597             tpls.body = new Roo.Template(
56598                '<table border="0" cellspacing="0" cellpadding="0">',
56599                "<tbody>{rows}</tbody>",
56600                "</table>"
56601             );
56602             tpls.body.disableFormats = true;
56603         }
56604         tpls.body.compile();
56605
56606         if(!tpls.row){
56607             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56608             tpls.row.disableFormats = true;
56609         }
56610         tpls.row.compile();
56611
56612         if(!tpls.cell){
56613             tpls.cell = new Roo.Template(
56614                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56615                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56616                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56617                 "</td>"
56618             );
56619             tpls.cell.disableFormats = true;
56620         }
56621         tpls.cell.compile();
56622
56623         this.templates = tpls;
56624     },
56625
56626     // remap these for backwards compat
56627     onColWidthChange : function(){
56628         this.updateColumns.apply(this, arguments);
56629     },
56630     onHeaderChange : function(){
56631         this.updateHeaders.apply(this, arguments);
56632     }, 
56633     onHiddenChange : function(){
56634         this.handleHiddenChange.apply(this, arguments);
56635     },
56636     onColumnMove : function(){
56637         this.handleColumnMove.apply(this, arguments);
56638     },
56639     onColumnLock : function(){
56640         this.handleLockChange.apply(this, arguments);
56641     },
56642
56643     onDataChange : function(){
56644         this.refresh();
56645         this.updateHeaderSortState();
56646     },
56647
56648     onClear : function(){
56649         this.refresh();
56650     },
56651
56652     onUpdate : function(ds, record){
56653         this.refreshRow(record);
56654     },
56655
56656     refreshRow : function(record){
56657         var ds = this.ds, index;
56658         if(typeof record == 'number'){
56659             index = record;
56660             record = ds.getAt(index);
56661         }else{
56662             index = ds.indexOf(record);
56663         }
56664         this.insertRows(ds, index, index, true);
56665         this.onRemove(ds, record, index+1, true);
56666         this.syncRowHeights(index, index);
56667         this.layout();
56668         this.fireEvent("rowupdated", this, index, record);
56669     },
56670
56671     onAdd : function(ds, records, index){
56672         this.insertRows(ds, index, index + (records.length-1));
56673     },
56674
56675     onRemove : function(ds, record, index, isUpdate){
56676         if(isUpdate !== true){
56677             this.fireEvent("beforerowremoved", this, index, record);
56678         }
56679         var bt = this.getBodyTable(), lt = this.getLockedTable();
56680         if(bt.rows[index]){
56681             bt.firstChild.removeChild(bt.rows[index]);
56682         }
56683         if(lt.rows[index]){
56684             lt.firstChild.removeChild(lt.rows[index]);
56685         }
56686         if(isUpdate !== true){
56687             this.stripeRows(index);
56688             this.syncRowHeights(index, index);
56689             this.layout();
56690             this.fireEvent("rowremoved", this, index, record);
56691         }
56692     },
56693
56694     onLoad : function(){
56695         this.scrollToTop();
56696     },
56697
56698     /**
56699      * Scrolls the grid to the top
56700      */
56701     scrollToTop : function(){
56702         if(this.scroller){
56703             this.scroller.dom.scrollTop = 0;
56704             this.syncScroll();
56705         }
56706     },
56707
56708     /**
56709      * Gets a panel in the header of the grid that can be used for toolbars etc.
56710      * After modifying the contents of this panel a call to grid.autoSize() may be
56711      * required to register any changes in size.
56712      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56713      * @return Roo.Element
56714      */
56715     getHeaderPanel : function(doShow){
56716         if(doShow){
56717             this.headerPanel.show();
56718         }
56719         return this.headerPanel;
56720     },
56721
56722     /**
56723      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56724      * After modifying the contents of this panel a call to grid.autoSize() may be
56725      * required to register any changes in size.
56726      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56727      * @return Roo.Element
56728      */
56729     getFooterPanel : function(doShow){
56730         if(doShow){
56731             this.footerPanel.show();
56732         }
56733         return this.footerPanel;
56734     },
56735
56736     initElements : function(){
56737         var E = Roo.Element;
56738         var el = this.grid.getGridEl().dom.firstChild;
56739         var cs = el.childNodes;
56740
56741         this.el = new E(el);
56742         
56743          this.focusEl = new E(el.firstChild);
56744         this.focusEl.swallowEvent("click", true);
56745         
56746         this.headerPanel = new E(cs[1]);
56747         this.headerPanel.enableDisplayMode("block");
56748
56749         this.scroller = new E(cs[2]);
56750         this.scrollSizer = new E(this.scroller.dom.firstChild);
56751
56752         this.lockedWrap = new E(cs[3]);
56753         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56754         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56755
56756         this.mainWrap = new E(cs[4]);
56757         this.mainHd = new E(this.mainWrap.dom.firstChild);
56758         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56759
56760         this.footerPanel = new E(cs[5]);
56761         this.footerPanel.enableDisplayMode("block");
56762
56763         this.resizeProxy = new E(cs[6]);
56764
56765         this.headerSelector = String.format(
56766            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56767            this.lockedHd.id, this.mainHd.id
56768         );
56769
56770         this.splitterSelector = String.format(
56771            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56772            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56773         );
56774     },
56775     idToCssName : function(s)
56776     {
56777         return s.replace(/[^a-z0-9]+/ig, '-');
56778     },
56779
56780     getHeaderCell : function(index){
56781         return Roo.DomQuery.select(this.headerSelector)[index];
56782     },
56783
56784     getHeaderCellMeasure : function(index){
56785         return this.getHeaderCell(index).firstChild;
56786     },
56787
56788     getHeaderCellText : function(index){
56789         return this.getHeaderCell(index).firstChild.firstChild;
56790     },
56791
56792     getLockedTable : function(){
56793         return this.lockedBody.dom.firstChild;
56794     },
56795
56796     getBodyTable : function(){
56797         return this.mainBody.dom.firstChild;
56798     },
56799
56800     getLockedRow : function(index){
56801         return this.getLockedTable().rows[index];
56802     },
56803
56804     getRow : function(index){
56805         return this.getBodyTable().rows[index];
56806     },
56807
56808     getRowComposite : function(index){
56809         if(!this.rowEl){
56810             this.rowEl = new Roo.CompositeElementLite();
56811         }
56812         var els = [], lrow, mrow;
56813         if(lrow = this.getLockedRow(index)){
56814             els.push(lrow);
56815         }
56816         if(mrow = this.getRow(index)){
56817             els.push(mrow);
56818         }
56819         this.rowEl.elements = els;
56820         return this.rowEl;
56821     },
56822     /**
56823      * Gets the 'td' of the cell
56824      * 
56825      * @param {Integer} rowIndex row to select
56826      * @param {Integer} colIndex column to select
56827      * 
56828      * @return {Object} 
56829      */
56830     getCell : function(rowIndex, colIndex){
56831         var locked = this.cm.getLockedCount();
56832         var source;
56833         if(colIndex < locked){
56834             source = this.lockedBody.dom.firstChild;
56835         }else{
56836             source = this.mainBody.dom.firstChild;
56837             colIndex -= locked;
56838         }
56839         return source.rows[rowIndex].childNodes[colIndex];
56840     },
56841
56842     getCellText : function(rowIndex, colIndex){
56843         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56844     },
56845
56846     getCellBox : function(cell){
56847         var b = this.fly(cell).getBox();
56848         if(Roo.isOpera){ // opera fails to report the Y
56849             b.y = cell.offsetTop + this.mainBody.getY();
56850         }
56851         return b;
56852     },
56853
56854     getCellIndex : function(cell){
56855         var id = String(cell.className).match(this.cellRE);
56856         if(id){
56857             return parseInt(id[1], 10);
56858         }
56859         return 0;
56860     },
56861
56862     findHeaderIndex : function(n){
56863         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56864         return r ? this.getCellIndex(r) : false;
56865     },
56866
56867     findHeaderCell : function(n){
56868         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56869         return r ? r : false;
56870     },
56871
56872     findRowIndex : function(n){
56873         if(!n){
56874             return false;
56875         }
56876         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56877         return r ? r.rowIndex : false;
56878     },
56879
56880     findCellIndex : function(node){
56881         var stop = this.el.dom;
56882         while(node && node != stop){
56883             if(this.findRE.test(node.className)){
56884                 return this.getCellIndex(node);
56885             }
56886             node = node.parentNode;
56887         }
56888         return false;
56889     },
56890
56891     getColumnId : function(index){
56892         return this.cm.getColumnId(index);
56893     },
56894
56895     getSplitters : function()
56896     {
56897         if(this.splitterSelector){
56898            return Roo.DomQuery.select(this.splitterSelector);
56899         }else{
56900             return null;
56901       }
56902     },
56903
56904     getSplitter : function(index){
56905         return this.getSplitters()[index];
56906     },
56907
56908     onRowOver : function(e, t){
56909         var row;
56910         if((row = this.findRowIndex(t)) !== false){
56911             this.getRowComposite(row).addClass("x-grid-row-over");
56912         }
56913     },
56914
56915     onRowOut : function(e, t){
56916         var row;
56917         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56918             this.getRowComposite(row).removeClass("x-grid-row-over");
56919         }
56920     },
56921
56922     renderHeaders : function(){
56923         var cm = this.cm;
56924         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56925         var cb = [], lb = [], sb = [], lsb = [], p = {};
56926         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56927             p.cellId = "x-grid-hd-0-" + i;
56928             p.splitId = "x-grid-csplit-0-" + i;
56929             p.id = cm.getColumnId(i);
56930             p.value = cm.getColumnHeader(i) || "";
56931             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56932             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56933             if(!cm.isLocked(i)){
56934                 cb[cb.length] = ct.apply(p);
56935                 sb[sb.length] = st.apply(p);
56936             }else{
56937                 lb[lb.length] = ct.apply(p);
56938                 lsb[lsb.length] = st.apply(p);
56939             }
56940         }
56941         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56942                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56943     },
56944
56945     updateHeaders : function(){
56946         var html = this.renderHeaders();
56947         this.lockedHd.update(html[0]);
56948         this.mainHd.update(html[1]);
56949     },
56950
56951     /**
56952      * Focuses the specified row.
56953      * @param {Number} row The row index
56954      */
56955     focusRow : function(row)
56956     {
56957         //Roo.log('GridView.focusRow');
56958         var x = this.scroller.dom.scrollLeft;
56959         this.focusCell(row, 0, false);
56960         this.scroller.dom.scrollLeft = x;
56961     },
56962
56963     /**
56964      * Focuses the specified cell.
56965      * @param {Number} row The row index
56966      * @param {Number} col The column index
56967      * @param {Boolean} hscroll false to disable horizontal scrolling
56968      */
56969     focusCell : function(row, col, hscroll)
56970     {
56971         //Roo.log('GridView.focusCell');
56972         var el = this.ensureVisible(row, col, hscroll);
56973         this.focusEl.alignTo(el, "tl-tl");
56974         if(Roo.isGecko){
56975             this.focusEl.focus();
56976         }else{
56977             this.focusEl.focus.defer(1, this.focusEl);
56978         }
56979     },
56980
56981     /**
56982      * Scrolls the specified cell into view
56983      * @param {Number} row The row index
56984      * @param {Number} col The column index
56985      * @param {Boolean} hscroll false to disable horizontal scrolling
56986      */
56987     ensureVisible : function(row, col, hscroll)
56988     {
56989         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56990         //return null; //disable for testing.
56991         if(typeof row != "number"){
56992             row = row.rowIndex;
56993         }
56994         if(row < 0 && row >= this.ds.getCount()){
56995             return  null;
56996         }
56997         col = (col !== undefined ? col : 0);
56998         var cm = this.grid.colModel;
56999         while(cm.isHidden(col)){
57000             col++;
57001         }
57002
57003         var el = this.getCell(row, col);
57004         if(!el){
57005             return null;
57006         }
57007         var c = this.scroller.dom;
57008
57009         var ctop = parseInt(el.offsetTop, 10);
57010         var cleft = parseInt(el.offsetLeft, 10);
57011         var cbot = ctop + el.offsetHeight;
57012         var cright = cleft + el.offsetWidth;
57013         
57014         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57015         var stop = parseInt(c.scrollTop, 10);
57016         var sleft = parseInt(c.scrollLeft, 10);
57017         var sbot = stop + ch;
57018         var sright = sleft + c.clientWidth;
57019         /*
57020         Roo.log('GridView.ensureVisible:' +
57021                 ' ctop:' + ctop +
57022                 ' c.clientHeight:' + c.clientHeight +
57023                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57024                 ' stop:' + stop +
57025                 ' cbot:' + cbot +
57026                 ' sbot:' + sbot +
57027                 ' ch:' + ch  
57028                 );
57029         */
57030         if(ctop < stop){
57031             c.scrollTop = ctop;
57032             //Roo.log("set scrolltop to ctop DISABLE?");
57033         }else if(cbot > sbot){
57034             //Roo.log("set scrolltop to cbot-ch");
57035             c.scrollTop = cbot-ch;
57036         }
57037         
57038         if(hscroll !== false){
57039             if(cleft < sleft){
57040                 c.scrollLeft = cleft;
57041             }else if(cright > sright){
57042                 c.scrollLeft = cright-c.clientWidth;
57043             }
57044         }
57045          
57046         return el;
57047     },
57048
57049     updateColumns : function(){
57050         this.grid.stopEditing();
57051         var cm = this.grid.colModel, colIds = this.getColumnIds();
57052         //var totalWidth = cm.getTotalWidth();
57053         var pos = 0;
57054         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57055             //if(cm.isHidden(i)) continue;
57056             var w = cm.getColumnWidth(i);
57057             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57058             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57059         }
57060         this.updateSplitters();
57061     },
57062
57063     generateRules : function(cm){
57064         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57065         Roo.util.CSS.removeStyleSheet(rulesId);
57066         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57067             var cid = cm.getColumnId(i);
57068             var align = '';
57069             if(cm.config[i].align){
57070                 align = 'text-align:'+cm.config[i].align+';';
57071             }
57072             var hidden = '';
57073             if(cm.isHidden(i)){
57074                 hidden = 'display:none;';
57075             }
57076             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57077             ruleBuf.push(
57078                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57079                     this.hdSelector, cid, " {\n", align, width, "}\n",
57080                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57081                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57082         }
57083         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57084     },
57085
57086     updateSplitters : function(){
57087         var cm = this.cm, s = this.getSplitters();
57088         if(s){ // splitters not created yet
57089             var pos = 0, locked = true;
57090             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57091                 if(cm.isHidden(i)) {
57092                     continue;
57093                 }
57094                 var w = cm.getColumnWidth(i); // make sure it's a number
57095                 if(!cm.isLocked(i) && locked){
57096                     pos = 0;
57097                     locked = false;
57098                 }
57099                 pos += w;
57100                 s[i].style.left = (pos-this.splitOffset) + "px";
57101             }
57102         }
57103     },
57104
57105     handleHiddenChange : function(colModel, colIndex, hidden){
57106         if(hidden){
57107             this.hideColumn(colIndex);
57108         }else{
57109             this.unhideColumn(colIndex);
57110         }
57111     },
57112
57113     hideColumn : function(colIndex){
57114         var cid = this.getColumnId(colIndex);
57115         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57116         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57117         if(Roo.isSafari){
57118             this.updateHeaders();
57119         }
57120         this.updateSplitters();
57121         this.layout();
57122     },
57123
57124     unhideColumn : function(colIndex){
57125         var cid = this.getColumnId(colIndex);
57126         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57127         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57128
57129         if(Roo.isSafari){
57130             this.updateHeaders();
57131         }
57132         this.updateSplitters();
57133         this.layout();
57134     },
57135
57136     insertRows : function(dm, firstRow, lastRow, isUpdate){
57137         if(firstRow == 0 && lastRow == dm.getCount()-1){
57138             this.refresh();
57139         }else{
57140             if(!isUpdate){
57141                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57142             }
57143             var s = this.getScrollState();
57144             var markup = this.renderRows(firstRow, lastRow);
57145             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57146             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57147             this.restoreScroll(s);
57148             if(!isUpdate){
57149                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57150                 this.syncRowHeights(firstRow, lastRow);
57151                 this.stripeRows(firstRow);
57152                 this.layout();
57153             }
57154         }
57155     },
57156
57157     bufferRows : function(markup, target, index){
57158         var before = null, trows = target.rows, tbody = target.tBodies[0];
57159         if(index < trows.length){
57160             before = trows[index];
57161         }
57162         var b = document.createElement("div");
57163         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57164         var rows = b.firstChild.rows;
57165         for(var i = 0, len = rows.length; i < len; i++){
57166             if(before){
57167                 tbody.insertBefore(rows[0], before);
57168             }else{
57169                 tbody.appendChild(rows[0]);
57170             }
57171         }
57172         b.innerHTML = "";
57173         b = null;
57174     },
57175
57176     deleteRows : function(dm, firstRow, lastRow){
57177         if(dm.getRowCount()<1){
57178             this.fireEvent("beforerefresh", this);
57179             this.mainBody.update("");
57180             this.lockedBody.update("");
57181             this.fireEvent("refresh", this);
57182         }else{
57183             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57184             var bt = this.getBodyTable();
57185             var tbody = bt.firstChild;
57186             var rows = bt.rows;
57187             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57188                 tbody.removeChild(rows[firstRow]);
57189             }
57190             this.stripeRows(firstRow);
57191             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57192         }
57193     },
57194
57195     updateRows : function(dataSource, firstRow, lastRow){
57196         var s = this.getScrollState();
57197         this.refresh();
57198         this.restoreScroll(s);
57199     },
57200
57201     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57202         if(!noRefresh){
57203            this.refresh();
57204         }
57205         this.updateHeaderSortState();
57206     },
57207
57208     getScrollState : function(){
57209         
57210         var sb = this.scroller.dom;
57211         return {left: sb.scrollLeft, top: sb.scrollTop};
57212     },
57213
57214     stripeRows : function(startRow){
57215         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57216             return;
57217         }
57218         startRow = startRow || 0;
57219         var rows = this.getBodyTable().rows;
57220         var lrows = this.getLockedTable().rows;
57221         var cls = ' x-grid-row-alt ';
57222         for(var i = startRow, len = rows.length; i < len; i++){
57223             var row = rows[i], lrow = lrows[i];
57224             var isAlt = ((i+1) % 2 == 0);
57225             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57226             if(isAlt == hasAlt){
57227                 continue;
57228             }
57229             if(isAlt){
57230                 row.className += " x-grid-row-alt";
57231             }else{
57232                 row.className = row.className.replace("x-grid-row-alt", "");
57233             }
57234             if(lrow){
57235                 lrow.className = row.className;
57236             }
57237         }
57238     },
57239
57240     restoreScroll : function(state){
57241         //Roo.log('GridView.restoreScroll');
57242         var sb = this.scroller.dom;
57243         sb.scrollLeft = state.left;
57244         sb.scrollTop = state.top;
57245         this.syncScroll();
57246     },
57247
57248     syncScroll : function(){
57249         //Roo.log('GridView.syncScroll');
57250         var sb = this.scroller.dom;
57251         var sh = this.mainHd.dom;
57252         var bs = this.mainBody.dom;
57253         var lv = this.lockedBody.dom;
57254         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57255         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57256     },
57257
57258     handleScroll : function(e){
57259         this.syncScroll();
57260         var sb = this.scroller.dom;
57261         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57262         e.stopEvent();
57263     },
57264
57265     handleWheel : function(e){
57266         var d = e.getWheelDelta();
57267         this.scroller.dom.scrollTop -= d*22;
57268         // set this here to prevent jumpy scrolling on large tables
57269         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57270         e.stopEvent();
57271     },
57272
57273     renderRows : function(startRow, endRow){
57274         // pull in all the crap needed to render rows
57275         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57276         var colCount = cm.getColumnCount();
57277
57278         if(ds.getCount() < 1){
57279             return ["", ""];
57280         }
57281
57282         // build a map for all the columns
57283         var cs = [];
57284         for(var i = 0; i < colCount; i++){
57285             var name = cm.getDataIndex(i);
57286             cs[i] = {
57287                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57288                 renderer : cm.getRenderer(i),
57289                 id : cm.getColumnId(i),
57290                 locked : cm.isLocked(i),
57291                 has_editor : cm.isCellEditable(i)
57292             };
57293         }
57294
57295         startRow = startRow || 0;
57296         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57297
57298         // records to render
57299         var rs = ds.getRange(startRow, endRow);
57300
57301         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57302     },
57303
57304     // As much as I hate to duplicate code, this was branched because FireFox really hates
57305     // [].join("") on strings. The performance difference was substantial enough to
57306     // branch this function
57307     doRender : Roo.isGecko ?
57308             function(cs, rs, ds, startRow, colCount, stripe){
57309                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57310                 // buffers
57311                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57312                 
57313                 var hasListener = this.grid.hasListener('rowclass');
57314                 var rowcfg = {};
57315                 for(var j = 0, len = rs.length; j < len; j++){
57316                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57317                     for(var i = 0; i < colCount; i++){
57318                         c = cs[i];
57319                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57320                         p.id = c.id;
57321                         p.css = p.attr = "";
57322                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57323                         if(p.value == undefined || p.value === "") {
57324                             p.value = "&#160;";
57325                         }
57326                         if(c.has_editor){
57327                             p.css += ' x-grid-editable-cell';
57328                         }
57329                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57330                             p.css +=  ' x-grid-dirty-cell';
57331                         }
57332                         var markup = ct.apply(p);
57333                         if(!c.locked){
57334                             cb+= markup;
57335                         }else{
57336                             lcb+= markup;
57337                         }
57338                     }
57339                     var alt = [];
57340                     if(stripe && ((rowIndex+1) % 2 == 0)){
57341                         alt.push("x-grid-row-alt")
57342                     }
57343                     if(r.dirty){
57344                         alt.push(  " x-grid-dirty-row");
57345                     }
57346                     rp.cells = lcb;
57347                     if(this.getRowClass){
57348                         alt.push(this.getRowClass(r, rowIndex));
57349                     }
57350                     if (hasListener) {
57351                         rowcfg = {
57352                              
57353                             record: r,
57354                             rowIndex : rowIndex,
57355                             rowClass : ''
57356                         };
57357                         this.grid.fireEvent('rowclass', this, rowcfg);
57358                         alt.push(rowcfg.rowClass);
57359                     }
57360                     rp.alt = alt.join(" ");
57361                     lbuf+= rt.apply(rp);
57362                     rp.cells = cb;
57363                     buf+=  rt.apply(rp);
57364                 }
57365                 return [lbuf, buf];
57366             } :
57367             function(cs, rs, ds, startRow, colCount, stripe){
57368                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57369                 // buffers
57370                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57371                 var hasListener = this.grid.hasListener('rowclass');
57372  
57373                 var rowcfg = {};
57374                 for(var j = 0, len = rs.length; j < len; j++){
57375                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57376                     for(var i = 0; i < colCount; i++){
57377                         c = cs[i];
57378                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57379                         p.id = c.id;
57380                         p.css = p.attr = "";
57381                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57382                         if(p.value == undefined || p.value === "") {
57383                             p.value = "&#160;";
57384                         }
57385                         //Roo.log(c);
57386                          if(c.has_editor){
57387                             p.css += ' x-grid-editable-cell';
57388                         }
57389                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57390                             p.css += ' x-grid-dirty-cell' 
57391                         }
57392                         
57393                         var markup = ct.apply(p);
57394                         if(!c.locked){
57395                             cb[cb.length] = markup;
57396                         }else{
57397                             lcb[lcb.length] = markup;
57398                         }
57399                     }
57400                     var alt = [];
57401                     if(stripe && ((rowIndex+1) % 2 == 0)){
57402                         alt.push( "x-grid-row-alt");
57403                     }
57404                     if(r.dirty){
57405                         alt.push(" x-grid-dirty-row");
57406                     }
57407                     rp.cells = lcb;
57408                     if(this.getRowClass){
57409                         alt.push( this.getRowClass(r, rowIndex));
57410                     }
57411                     if (hasListener) {
57412                         rowcfg = {
57413                              
57414                             record: r,
57415                             rowIndex : rowIndex,
57416                             rowClass : ''
57417                         };
57418                         this.grid.fireEvent('rowclass', this, rowcfg);
57419                         alt.push(rowcfg.rowClass);
57420                     }
57421                     
57422                     rp.alt = alt.join(" ");
57423                     rp.cells = lcb.join("");
57424                     lbuf[lbuf.length] = rt.apply(rp);
57425                     rp.cells = cb.join("");
57426                     buf[buf.length] =  rt.apply(rp);
57427                 }
57428                 return [lbuf.join(""), buf.join("")];
57429             },
57430
57431     renderBody : function(){
57432         var markup = this.renderRows();
57433         var bt = this.templates.body;
57434         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57435     },
57436
57437     /**
57438      * Refreshes the grid
57439      * @param {Boolean} headersToo
57440      */
57441     refresh : function(headersToo){
57442         this.fireEvent("beforerefresh", this);
57443         this.grid.stopEditing();
57444         var result = this.renderBody();
57445         this.lockedBody.update(result[0]);
57446         this.mainBody.update(result[1]);
57447         if(headersToo === true){
57448             this.updateHeaders();
57449             this.updateColumns();
57450             this.updateSplitters();
57451             this.updateHeaderSortState();
57452         }
57453         this.syncRowHeights();
57454         this.layout();
57455         this.fireEvent("refresh", this);
57456     },
57457
57458     handleColumnMove : function(cm, oldIndex, newIndex){
57459         this.indexMap = null;
57460         var s = this.getScrollState();
57461         this.refresh(true);
57462         this.restoreScroll(s);
57463         this.afterMove(newIndex);
57464     },
57465
57466     afterMove : function(colIndex){
57467         if(this.enableMoveAnim && Roo.enableFx){
57468             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57469         }
57470         // if multisort - fix sortOrder, and reload..
57471         if (this.grid.dataSource.multiSort) {
57472             // the we can call sort again..
57473             var dm = this.grid.dataSource;
57474             var cm = this.grid.colModel;
57475             var so = [];
57476             for(var i = 0; i < cm.config.length; i++ ) {
57477                 
57478                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57479                     continue; // dont' bother, it's not in sort list or being set.
57480                 }
57481                 
57482                 so.push(cm.config[i].dataIndex);
57483             };
57484             dm.sortOrder = so;
57485             dm.load(dm.lastOptions);
57486             
57487             
57488         }
57489         
57490     },
57491
57492     updateCell : function(dm, rowIndex, dataIndex){
57493         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57494         if(typeof colIndex == "undefined"){ // not present in grid
57495             return;
57496         }
57497         var cm = this.grid.colModel;
57498         var cell = this.getCell(rowIndex, colIndex);
57499         var cellText = this.getCellText(rowIndex, colIndex);
57500
57501         var p = {
57502             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57503             id : cm.getColumnId(colIndex),
57504             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57505         };
57506         var renderer = cm.getRenderer(colIndex);
57507         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57508         if(typeof val == "undefined" || val === "") {
57509             val = "&#160;";
57510         }
57511         cellText.innerHTML = val;
57512         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57513         this.syncRowHeights(rowIndex, rowIndex);
57514     },
57515
57516     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57517         var maxWidth = 0;
57518         if(this.grid.autoSizeHeaders){
57519             var h = this.getHeaderCellMeasure(colIndex);
57520             maxWidth = Math.max(maxWidth, h.scrollWidth);
57521         }
57522         var tb, index;
57523         if(this.cm.isLocked(colIndex)){
57524             tb = this.getLockedTable();
57525             index = colIndex;
57526         }else{
57527             tb = this.getBodyTable();
57528             index = colIndex - this.cm.getLockedCount();
57529         }
57530         if(tb && tb.rows){
57531             var rows = tb.rows;
57532             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57533             for(var i = 0; i < stopIndex; i++){
57534                 var cell = rows[i].childNodes[index].firstChild;
57535                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57536             }
57537         }
57538         return maxWidth + /*margin for error in IE*/ 5;
57539     },
57540     /**
57541      * Autofit a column to its content.
57542      * @param {Number} colIndex
57543      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57544      */
57545      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57546          if(this.cm.isHidden(colIndex)){
57547              return; // can't calc a hidden column
57548          }
57549         if(forceMinSize){
57550             var cid = this.cm.getColumnId(colIndex);
57551             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57552            if(this.grid.autoSizeHeaders){
57553                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57554            }
57555         }
57556         var newWidth = this.calcColumnWidth(colIndex);
57557         this.cm.setColumnWidth(colIndex,
57558             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57559         if(!suppressEvent){
57560             this.grid.fireEvent("columnresize", colIndex, newWidth);
57561         }
57562     },
57563
57564     /**
57565      * Autofits all columns to their content and then expands to fit any extra space in the grid
57566      */
57567      autoSizeColumns : function(){
57568         var cm = this.grid.colModel;
57569         var colCount = cm.getColumnCount();
57570         for(var i = 0; i < colCount; i++){
57571             this.autoSizeColumn(i, true, true);
57572         }
57573         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57574             this.fitColumns();
57575         }else{
57576             this.updateColumns();
57577             this.layout();
57578         }
57579     },
57580
57581     /**
57582      * Autofits all columns to the grid's width proportionate with their current size
57583      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57584      */
57585     fitColumns : function(reserveScrollSpace){
57586         var cm = this.grid.colModel;
57587         var colCount = cm.getColumnCount();
57588         var cols = [];
57589         var width = 0;
57590         var i, w;
57591         for (i = 0; i < colCount; i++){
57592             if(!cm.isHidden(i) && !cm.isFixed(i)){
57593                 w = cm.getColumnWidth(i);
57594                 cols.push(i);
57595                 cols.push(w);
57596                 width += w;
57597             }
57598         }
57599         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57600         if(reserveScrollSpace){
57601             avail -= 17;
57602         }
57603         var frac = (avail - cm.getTotalWidth())/width;
57604         while (cols.length){
57605             w = cols.pop();
57606             i = cols.pop();
57607             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57608         }
57609         this.updateColumns();
57610         this.layout();
57611     },
57612
57613     onRowSelect : function(rowIndex){
57614         var row = this.getRowComposite(rowIndex);
57615         row.addClass("x-grid-row-selected");
57616     },
57617
57618     onRowDeselect : function(rowIndex){
57619         var row = this.getRowComposite(rowIndex);
57620         row.removeClass("x-grid-row-selected");
57621     },
57622
57623     onCellSelect : function(row, col){
57624         var cell = this.getCell(row, col);
57625         if(cell){
57626             Roo.fly(cell).addClass("x-grid-cell-selected");
57627         }
57628     },
57629
57630     onCellDeselect : function(row, col){
57631         var cell = this.getCell(row, col);
57632         if(cell){
57633             Roo.fly(cell).removeClass("x-grid-cell-selected");
57634         }
57635     },
57636
57637     updateHeaderSortState : function(){
57638         
57639         // sort state can be single { field: xxx, direction : yyy}
57640         // or   { xxx=>ASC , yyy : DESC ..... }
57641         
57642         var mstate = {};
57643         if (!this.ds.multiSort) { 
57644             var state = this.ds.getSortState();
57645             if(!state){
57646                 return;
57647             }
57648             mstate[state.field] = state.direction;
57649             // FIXME... - this is not used here.. but might be elsewhere..
57650             this.sortState = state;
57651             
57652         } else {
57653             mstate = this.ds.sortToggle;
57654         }
57655         //remove existing sort classes..
57656         
57657         var sc = this.sortClasses;
57658         var hds = this.el.select(this.headerSelector).removeClass(sc);
57659         
57660         for(var f in mstate) {
57661         
57662             var sortColumn = this.cm.findColumnIndex(f);
57663             
57664             if(sortColumn != -1){
57665                 var sortDir = mstate[f];        
57666                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57667             }
57668         }
57669         
57670          
57671         
57672     },
57673
57674
57675     handleHeaderClick : function(g, index,e){
57676         
57677         Roo.log("header click");
57678         
57679         if (Roo.isTouch) {
57680             // touch events on header are handled by context
57681             this.handleHdCtx(g,index,e);
57682             return;
57683         }
57684         
57685         
57686         if(this.headersDisabled){
57687             return;
57688         }
57689         var dm = g.dataSource, cm = g.colModel;
57690         if(!cm.isSortable(index)){
57691             return;
57692         }
57693         g.stopEditing();
57694         
57695         if (dm.multiSort) {
57696             // update the sortOrder
57697             var so = [];
57698             for(var i = 0; i < cm.config.length; i++ ) {
57699                 
57700                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57701                     continue; // dont' bother, it's not in sort list or being set.
57702                 }
57703                 
57704                 so.push(cm.config[i].dataIndex);
57705             };
57706             dm.sortOrder = so;
57707         }
57708         
57709         
57710         dm.sort(cm.getDataIndex(index));
57711     },
57712
57713
57714     destroy : function(){
57715         if(this.colMenu){
57716             this.colMenu.removeAll();
57717             Roo.menu.MenuMgr.unregister(this.colMenu);
57718             this.colMenu.getEl().remove();
57719             delete this.colMenu;
57720         }
57721         if(this.hmenu){
57722             this.hmenu.removeAll();
57723             Roo.menu.MenuMgr.unregister(this.hmenu);
57724             this.hmenu.getEl().remove();
57725             delete this.hmenu;
57726         }
57727         if(this.grid.enableColumnMove){
57728             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57729             if(dds){
57730                 for(var dd in dds){
57731                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57732                         var elid = dds[dd].dragElId;
57733                         dds[dd].unreg();
57734                         Roo.get(elid).remove();
57735                     } else if(dds[dd].config.isTarget){
57736                         dds[dd].proxyTop.remove();
57737                         dds[dd].proxyBottom.remove();
57738                         dds[dd].unreg();
57739                     }
57740                     if(Roo.dd.DDM.locationCache[dd]){
57741                         delete Roo.dd.DDM.locationCache[dd];
57742                     }
57743                 }
57744                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57745             }
57746         }
57747         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57748         this.bind(null, null);
57749         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57750     },
57751
57752     handleLockChange : function(){
57753         this.refresh(true);
57754     },
57755
57756     onDenyColumnLock : function(){
57757
57758     },
57759
57760     onDenyColumnHide : function(){
57761
57762     },
57763
57764     handleHdMenuClick : function(item){
57765         var index = this.hdCtxIndex;
57766         var cm = this.cm, ds = this.ds;
57767         switch(item.id){
57768             case "asc":
57769                 ds.sort(cm.getDataIndex(index), "ASC");
57770                 break;
57771             case "desc":
57772                 ds.sort(cm.getDataIndex(index), "DESC");
57773                 break;
57774             case "lock":
57775                 var lc = cm.getLockedCount();
57776                 if(cm.getColumnCount(true) <= lc+1){
57777                     this.onDenyColumnLock();
57778                     return;
57779                 }
57780                 if(lc != index){
57781                     cm.setLocked(index, true, true);
57782                     cm.moveColumn(index, lc);
57783                     this.grid.fireEvent("columnmove", index, lc);
57784                 }else{
57785                     cm.setLocked(index, true);
57786                 }
57787             break;
57788             case "unlock":
57789                 var lc = cm.getLockedCount();
57790                 if((lc-1) != index){
57791                     cm.setLocked(index, false, true);
57792                     cm.moveColumn(index, lc-1);
57793                     this.grid.fireEvent("columnmove", index, lc-1);
57794                 }else{
57795                     cm.setLocked(index, false);
57796                 }
57797             break;
57798             case 'wider': // used to expand cols on touch..
57799             case 'narrow':
57800                 var cw = cm.getColumnWidth(index);
57801                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57802                 cw = Math.max(0, cw);
57803                 cw = Math.min(cw,4000);
57804                 cm.setColumnWidth(index, cw);
57805                 break;
57806                 
57807             default:
57808                 index = cm.getIndexById(item.id.substr(4));
57809                 if(index != -1){
57810                     if(item.checked && cm.getColumnCount(true) <= 1){
57811                         this.onDenyColumnHide();
57812                         return false;
57813                     }
57814                     cm.setHidden(index, item.checked);
57815                 }
57816         }
57817         return true;
57818     },
57819
57820     beforeColMenuShow : function(){
57821         var cm = this.cm,  colCount = cm.getColumnCount();
57822         this.colMenu.removeAll();
57823         for(var i = 0; i < colCount; i++){
57824             this.colMenu.add(new Roo.menu.CheckItem({
57825                 id: "col-"+cm.getColumnId(i),
57826                 text: cm.getColumnHeader(i),
57827                 checked: !cm.isHidden(i),
57828                 hideOnClick:false
57829             }));
57830         }
57831     },
57832
57833     handleHdCtx : function(g, index, e){
57834         e.stopEvent();
57835         var hd = this.getHeaderCell(index);
57836         this.hdCtxIndex = index;
57837         var ms = this.hmenu.items, cm = this.cm;
57838         ms.get("asc").setDisabled(!cm.isSortable(index));
57839         ms.get("desc").setDisabled(!cm.isSortable(index));
57840         if(this.grid.enableColLock !== false){
57841             ms.get("lock").setDisabled(cm.isLocked(index));
57842             ms.get("unlock").setDisabled(!cm.isLocked(index));
57843         }
57844         this.hmenu.show(hd, "tl-bl");
57845     },
57846
57847     handleHdOver : function(e){
57848         var hd = this.findHeaderCell(e.getTarget());
57849         if(hd && !this.headersDisabled){
57850             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57851                this.fly(hd).addClass("x-grid-hd-over");
57852             }
57853         }
57854     },
57855
57856     handleHdOut : function(e){
57857         var hd = this.findHeaderCell(e.getTarget());
57858         if(hd){
57859             this.fly(hd).removeClass("x-grid-hd-over");
57860         }
57861     },
57862
57863     handleSplitDblClick : function(e, t){
57864         var i = this.getCellIndex(t);
57865         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57866             this.autoSizeColumn(i, true);
57867             this.layout();
57868         }
57869     },
57870
57871     render : function(){
57872
57873         var cm = this.cm;
57874         var colCount = cm.getColumnCount();
57875
57876         if(this.grid.monitorWindowResize === true){
57877             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57878         }
57879         var header = this.renderHeaders();
57880         var body = this.templates.body.apply({rows:""});
57881         var html = this.templates.master.apply({
57882             lockedBody: body,
57883             body: body,
57884             lockedHeader: header[0],
57885             header: header[1]
57886         });
57887
57888         //this.updateColumns();
57889
57890         this.grid.getGridEl().dom.innerHTML = html;
57891
57892         this.initElements();
57893         
57894         // a kludge to fix the random scolling effect in webkit
57895         this.el.on("scroll", function() {
57896             this.el.dom.scrollTop=0; // hopefully not recursive..
57897         },this);
57898
57899         this.scroller.on("scroll", this.handleScroll, this);
57900         this.lockedBody.on("mousewheel", this.handleWheel, this);
57901         this.mainBody.on("mousewheel", this.handleWheel, this);
57902
57903         this.mainHd.on("mouseover", this.handleHdOver, this);
57904         this.mainHd.on("mouseout", this.handleHdOut, this);
57905         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57906                 {delegate: "."+this.splitClass});
57907
57908         this.lockedHd.on("mouseover", this.handleHdOver, this);
57909         this.lockedHd.on("mouseout", this.handleHdOut, this);
57910         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57911                 {delegate: "."+this.splitClass});
57912
57913         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57914             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57915         }
57916
57917         this.updateSplitters();
57918
57919         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57920             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57921             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57922         }
57923
57924         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57925             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57926             this.hmenu.add(
57927                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57928                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57929             );
57930             if(this.grid.enableColLock !== false){
57931                 this.hmenu.add('-',
57932                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57933                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57934                 );
57935             }
57936             if (Roo.isTouch) {
57937                  this.hmenu.add('-',
57938                     {id:"wider", text: this.columnsWiderText},
57939                     {id:"narrow", text: this.columnsNarrowText }
57940                 );
57941                 
57942                  
57943             }
57944             
57945             if(this.grid.enableColumnHide !== false){
57946
57947                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57948                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57949                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57950
57951                 this.hmenu.add('-',
57952                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57953                 );
57954             }
57955             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57956
57957             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57958         }
57959
57960         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57961             this.dd = new Roo.grid.GridDragZone(this.grid, {
57962                 ddGroup : this.grid.ddGroup || 'GridDD'
57963             });
57964             
57965         }
57966
57967         /*
57968         for(var i = 0; i < colCount; i++){
57969             if(cm.isHidden(i)){
57970                 this.hideColumn(i);
57971             }
57972             if(cm.config[i].align){
57973                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57974                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57975             }
57976         }*/
57977         
57978         this.updateHeaderSortState();
57979
57980         this.beforeInitialResize();
57981         this.layout(true);
57982
57983         // two part rendering gives faster view to the user
57984         this.renderPhase2.defer(1, this);
57985     },
57986
57987     renderPhase2 : function(){
57988         // render the rows now
57989         this.refresh();
57990         if(this.grid.autoSizeColumns){
57991             this.autoSizeColumns();
57992         }
57993     },
57994
57995     beforeInitialResize : function(){
57996
57997     },
57998
57999     onColumnSplitterMoved : function(i, w){
58000         this.userResized = true;
58001         var cm = this.grid.colModel;
58002         cm.setColumnWidth(i, w, true);
58003         var cid = cm.getColumnId(i);
58004         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58005         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58006         this.updateSplitters();
58007         this.layout();
58008         this.grid.fireEvent("columnresize", i, w);
58009     },
58010
58011     syncRowHeights : function(startIndex, endIndex){
58012         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58013             startIndex = startIndex || 0;
58014             var mrows = this.getBodyTable().rows;
58015             var lrows = this.getLockedTable().rows;
58016             var len = mrows.length-1;
58017             endIndex = Math.min(endIndex || len, len);
58018             for(var i = startIndex; i <= endIndex; i++){
58019                 var m = mrows[i], l = lrows[i];
58020                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58021                 m.style.height = l.style.height = h + "px";
58022             }
58023         }
58024     },
58025
58026     layout : function(initialRender, is2ndPass)
58027     {
58028         var g = this.grid;
58029         var auto = g.autoHeight;
58030         var scrollOffset = 16;
58031         var c = g.getGridEl(), cm = this.cm,
58032                 expandCol = g.autoExpandColumn,
58033                 gv = this;
58034         //c.beginMeasure();
58035
58036         if(!c.dom.offsetWidth){ // display:none?
58037             if(initialRender){
58038                 this.lockedWrap.show();
58039                 this.mainWrap.show();
58040             }
58041             return;
58042         }
58043
58044         var hasLock = this.cm.isLocked(0);
58045
58046         var tbh = this.headerPanel.getHeight();
58047         var bbh = this.footerPanel.getHeight();
58048
58049         if(auto){
58050             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58051             var newHeight = ch + c.getBorderWidth("tb");
58052             if(g.maxHeight){
58053                 newHeight = Math.min(g.maxHeight, newHeight);
58054             }
58055             c.setHeight(newHeight);
58056         }
58057
58058         if(g.autoWidth){
58059             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58060         }
58061
58062         var s = this.scroller;
58063
58064         var csize = c.getSize(true);
58065
58066         this.el.setSize(csize.width, csize.height);
58067
58068         this.headerPanel.setWidth(csize.width);
58069         this.footerPanel.setWidth(csize.width);
58070
58071         var hdHeight = this.mainHd.getHeight();
58072         var vw = csize.width;
58073         var vh = csize.height - (tbh + bbh);
58074
58075         s.setSize(vw, vh);
58076
58077         var bt = this.getBodyTable();
58078         
58079         if(cm.getLockedCount() == cm.config.length){
58080             bt = this.getLockedTable();
58081         }
58082         
58083         var ltWidth = hasLock ?
58084                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58085
58086         var scrollHeight = bt.offsetHeight;
58087         var scrollWidth = ltWidth + bt.offsetWidth;
58088         var vscroll = false, hscroll = false;
58089
58090         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58091
58092         var lw = this.lockedWrap, mw = this.mainWrap;
58093         var lb = this.lockedBody, mb = this.mainBody;
58094
58095         setTimeout(function(){
58096             var t = s.dom.offsetTop;
58097             var w = s.dom.clientWidth,
58098                 h = s.dom.clientHeight;
58099
58100             lw.setTop(t);
58101             lw.setSize(ltWidth, h);
58102
58103             mw.setLeftTop(ltWidth, t);
58104             mw.setSize(w-ltWidth, h);
58105
58106             lb.setHeight(h-hdHeight);
58107             mb.setHeight(h-hdHeight);
58108
58109             if(is2ndPass !== true && !gv.userResized && expandCol){
58110                 // high speed resize without full column calculation
58111                 
58112                 var ci = cm.getIndexById(expandCol);
58113                 if (ci < 0) {
58114                     ci = cm.findColumnIndex(expandCol);
58115                 }
58116                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58117                 var expandId = cm.getColumnId(ci);
58118                 var  tw = cm.getTotalWidth(false);
58119                 var currentWidth = cm.getColumnWidth(ci);
58120                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58121                 if(currentWidth != cw){
58122                     cm.setColumnWidth(ci, cw, true);
58123                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58124                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58125                     gv.updateSplitters();
58126                     gv.layout(false, true);
58127                 }
58128             }
58129
58130             if(initialRender){
58131                 lw.show();
58132                 mw.show();
58133             }
58134             //c.endMeasure();
58135         }, 10);
58136     },
58137
58138     onWindowResize : function(){
58139         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58140             return;
58141         }
58142         this.layout();
58143     },
58144
58145     appendFooter : function(parentEl){
58146         return null;
58147     },
58148
58149     sortAscText : "Sort Ascending",
58150     sortDescText : "Sort Descending",
58151     lockText : "Lock Column",
58152     unlockText : "Unlock Column",
58153     columnsText : "Columns",
58154  
58155     columnsWiderText : "Wider",
58156     columnsNarrowText : "Thinner"
58157 });
58158
58159
58160 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58161     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58162     this.proxy.el.addClass('x-grid3-col-dd');
58163 };
58164
58165 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58166     handleMouseDown : function(e){
58167
58168     },
58169
58170     callHandleMouseDown : function(e){
58171         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58172     }
58173 });
58174 /*
58175  * Based on:
58176  * Ext JS Library 1.1.1
58177  * Copyright(c) 2006-2007, Ext JS, LLC.
58178  *
58179  * Originally Released Under LGPL - original licence link has changed is not relivant.
58180  *
58181  * Fork - LGPL
58182  * <script type="text/javascript">
58183  */
58184  /**
58185  * @extends Roo.dd.DDProxy
58186  * @class Roo.grid.SplitDragZone
58187  * Support for Column Header resizing
58188  * @constructor
58189  * @param {Object} config
58190  */
58191 // private
58192 // This is a support class used internally by the Grid components
58193 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58194     this.grid = grid;
58195     this.view = grid.getView();
58196     this.proxy = this.view.resizeProxy;
58197     Roo.grid.SplitDragZone.superclass.constructor.call(
58198         this,
58199         hd, // ID
58200         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58201         {  // CONFIG
58202             dragElId : Roo.id(this.proxy.dom),
58203             resizeFrame:false
58204         }
58205     );
58206     
58207     this.setHandleElId(Roo.id(hd));
58208     if (hd2 !== false) {
58209         this.setOuterHandleElId(Roo.id(hd2));
58210     }
58211     
58212     this.scroll = false;
58213 };
58214 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58215     fly: Roo.Element.fly,
58216
58217     b4StartDrag : function(x, y){
58218         this.view.headersDisabled = true;
58219         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58220                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58221         );
58222         this.proxy.setHeight(h);
58223         
58224         // for old system colWidth really stored the actual width?
58225         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58226         // which in reality did not work.. - it worked only for fixed sizes
58227         // for resizable we need to use actual sizes.
58228         var w = this.cm.getColumnWidth(this.cellIndex);
58229         if (!this.view.mainWrap) {
58230             // bootstrap.
58231             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58232         }
58233         
58234         
58235         
58236         // this was w-this.grid.minColumnWidth;
58237         // doesnt really make sense? - w = thie curren width or the rendered one?
58238         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58239         this.resetConstraints();
58240         this.setXConstraint(minw, 1000);
58241         this.setYConstraint(0, 0);
58242         this.minX = x - minw;
58243         this.maxX = x + 1000;
58244         this.startPos = x;
58245         if (!this.view.mainWrap) { // this is Bootstrap code..
58246             this.getDragEl().style.display='block';
58247         }
58248         
58249         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58250     },
58251
58252
58253     handleMouseDown : function(e){
58254         ev = Roo.EventObject.setEvent(e);
58255         var t = this.fly(ev.getTarget());
58256         if(t.hasClass("x-grid-split")){
58257             this.cellIndex = this.view.getCellIndex(t.dom);
58258             this.split = t.dom;
58259             this.cm = this.grid.colModel;
58260             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58261                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58262             }
58263         }
58264     },
58265
58266     endDrag : function(e){
58267         this.view.headersDisabled = false;
58268         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58269         var diff = endX - this.startPos;
58270         // 
58271         var w = this.cm.getColumnWidth(this.cellIndex);
58272         if (!this.view.mainWrap) {
58273             w = 0;
58274         }
58275         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58276     },
58277
58278     autoOffset : function(){
58279         this.setDelta(0,0);
58280     }
58281 });/*
58282  * Based on:
58283  * Ext JS Library 1.1.1
58284  * Copyright(c) 2006-2007, Ext JS, LLC.
58285  *
58286  * Originally Released Under LGPL - original licence link has changed is not relivant.
58287  *
58288  * Fork - LGPL
58289  * <script type="text/javascript">
58290  */
58291  
58292 // private
58293 // This is a support class used internally by the Grid components
58294 Roo.grid.GridDragZone = function(grid, config){
58295     this.view = grid.getView();
58296     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58297     if(this.view.lockedBody){
58298         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58299         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58300     }
58301     this.scroll = false;
58302     this.grid = grid;
58303     this.ddel = document.createElement('div');
58304     this.ddel.className = 'x-grid-dd-wrap';
58305 };
58306
58307 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58308     ddGroup : "GridDD",
58309
58310     getDragData : function(e){
58311         var t = Roo.lib.Event.getTarget(e);
58312         var rowIndex = this.view.findRowIndex(t);
58313         var sm = this.grid.selModel;
58314             
58315         //Roo.log(rowIndex);
58316         
58317         if (sm.getSelectedCell) {
58318             // cell selection..
58319             if (!sm.getSelectedCell()) {
58320                 return false;
58321             }
58322             if (rowIndex != sm.getSelectedCell()[0]) {
58323                 return false;
58324             }
58325         
58326         }
58327         if (sm.getSelections && sm.getSelections().length < 1) {
58328             return false;
58329         }
58330         
58331         
58332         // before it used to all dragging of unseleted... - now we dont do that.
58333         if(rowIndex !== false){
58334             
58335             // if editorgrid.. 
58336             
58337             
58338             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58339                
58340             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58341               //  
58342             //}
58343             if (e.hasModifier()){
58344                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58345             }
58346             
58347             Roo.log("getDragData");
58348             
58349             return {
58350                 grid: this.grid,
58351                 ddel: this.ddel,
58352                 rowIndex: rowIndex,
58353                 selections: sm.getSelections ? sm.getSelections() : (
58354                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58355             };
58356         }
58357         return false;
58358     },
58359     
58360     
58361     onInitDrag : function(e){
58362         var data = this.dragData;
58363         this.ddel.innerHTML = this.grid.getDragDropText();
58364         this.proxy.update(this.ddel);
58365         // fire start drag?
58366     },
58367
58368     afterRepair : function(){
58369         this.dragging = false;
58370     },
58371
58372     getRepairXY : function(e, data){
58373         return false;
58374     },
58375
58376     onEndDrag : function(data, e){
58377         // fire end drag?
58378     },
58379
58380     onValidDrop : function(dd, e, id){
58381         // fire drag drop?
58382         this.hideProxy();
58383     },
58384
58385     beforeInvalidDrop : function(e, id){
58386
58387     }
58388 });/*
58389  * Based on:
58390  * Ext JS Library 1.1.1
58391  * Copyright(c) 2006-2007, Ext JS, LLC.
58392  *
58393  * Originally Released Under LGPL - original licence link has changed is not relivant.
58394  *
58395  * Fork - LGPL
58396  * <script type="text/javascript">
58397  */
58398  
58399
58400 /**
58401  * @class Roo.grid.ColumnModel
58402  * @extends Roo.util.Observable
58403  * This is the default implementation of a ColumnModel used by the Grid. It defines
58404  * the columns in the grid.
58405  * <br>Usage:<br>
58406  <pre><code>
58407  var colModel = new Roo.grid.ColumnModel([
58408         {header: "Ticker", width: 60, sortable: true, locked: true},
58409         {header: "Company Name", width: 150, sortable: true},
58410         {header: "Market Cap.", width: 100, sortable: true},
58411         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58412         {header: "Employees", width: 100, sortable: true, resizable: false}
58413  ]);
58414  </code></pre>
58415  * <p>
58416  
58417  * The config options listed for this class are options which may appear in each
58418  * individual column definition.
58419  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58420  * @constructor
58421  * @param {Object} config An Array of column config objects. See this class's
58422  * config objects for details.
58423 */
58424 Roo.grid.ColumnModel = function(config){
58425         /**
58426      * The config passed into the constructor
58427      */
58428     this.config = []; //config;
58429     this.lookup = {};
58430
58431     // if no id, create one
58432     // if the column does not have a dataIndex mapping,
58433     // map it to the order it is in the config
58434     for(var i = 0, len = config.length; i < len; i++){
58435         this.addColumn(config[i]);
58436         
58437     }
58438
58439     /**
58440      * The width of columns which have no width specified (defaults to 100)
58441      * @type Number
58442      */
58443     this.defaultWidth = 100;
58444
58445     /**
58446      * Default sortable of columns which have no sortable specified (defaults to false)
58447      * @type Boolean
58448      */
58449     this.defaultSortable = false;
58450
58451     this.addEvents({
58452         /**
58453              * @event widthchange
58454              * Fires when the width of a column changes.
58455              * @param {ColumnModel} this
58456              * @param {Number} columnIndex The column index
58457              * @param {Number} newWidth The new width
58458              */
58459             "widthchange": true,
58460         /**
58461              * @event headerchange
58462              * Fires when the text of a header changes.
58463              * @param {ColumnModel} this
58464              * @param {Number} columnIndex The column index
58465              * @param {Number} newText The new header text
58466              */
58467             "headerchange": true,
58468         /**
58469              * @event hiddenchange
58470              * Fires when a column is hidden or "unhidden".
58471              * @param {ColumnModel} this
58472              * @param {Number} columnIndex The column index
58473              * @param {Boolean} hidden true if hidden, false otherwise
58474              */
58475             "hiddenchange": true,
58476             /**
58477          * @event columnmoved
58478          * Fires when a column is moved.
58479          * @param {ColumnModel} this
58480          * @param {Number} oldIndex
58481          * @param {Number} newIndex
58482          */
58483         "columnmoved" : true,
58484         /**
58485          * @event columlockchange
58486          * Fires when a column's locked state is changed
58487          * @param {ColumnModel} this
58488          * @param {Number} colIndex
58489          * @param {Boolean} locked true if locked
58490          */
58491         "columnlockchange" : true
58492     });
58493     Roo.grid.ColumnModel.superclass.constructor.call(this);
58494 };
58495 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58496     /**
58497      * @cfg {String} header The header text to display in the Grid view.
58498      */
58499         /**
58500      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58501      */
58502         /**
58503      * @cfg {String} smHeader Header at Bootsrap Small width
58504      */
58505         /**
58506      * @cfg {String} mdHeader Header at Bootsrap Medium width
58507      */
58508         /**
58509      * @cfg {String} lgHeader Header at Bootsrap Large width
58510      */
58511         /**
58512      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58513      */
58514     /**
58515      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58516      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58517      * specified, the column's index is used as an index into the Record's data Array.
58518      */
58519     /**
58520      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58521      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58522      */
58523     /**
58524      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58525      * Defaults to the value of the {@link #defaultSortable} property.
58526      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58527      */
58528     /**
58529      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58530      */
58531     /**
58532      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58533      */
58534     /**
58535      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58536      */
58537     /**
58538      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58539      */
58540     /**
58541      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58542      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58543      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58544      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58545      */
58546        /**
58547      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58548      */
58549     /**
58550      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58551      */
58552     /**
58553      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58554      */
58555     /**
58556      * @cfg {String} cursor (Optional)
58557      */
58558     /**
58559      * @cfg {String} tooltip (Optional)
58560      */
58561     /**
58562      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58563      */
58564     /**
58565      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58566      */
58567     /**
58568      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58569      */
58570     /**
58571      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58572      */
58573         /**
58574      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58575      */
58576     /**
58577      * Returns the id of the column at the specified index.
58578      * @param {Number} index The column index
58579      * @return {String} the id
58580      */
58581     getColumnId : function(index){
58582         return this.config[index].id;
58583     },
58584
58585     /**
58586      * Returns the column for a specified id.
58587      * @param {String} id The column id
58588      * @return {Object} the column
58589      */
58590     getColumnById : function(id){
58591         return this.lookup[id];
58592     },
58593
58594     
58595     /**
58596      * Returns the column Object for a specified dataIndex.
58597      * @param {String} dataIndex The column dataIndex
58598      * @return {Object|Boolean} the column or false if not found
58599      */
58600     getColumnByDataIndex: function(dataIndex){
58601         var index = this.findColumnIndex(dataIndex);
58602         return index > -1 ? this.config[index] : false;
58603     },
58604     
58605     /**
58606      * Returns the index for a specified column id.
58607      * @param {String} id The column id
58608      * @return {Number} the index, or -1 if not found
58609      */
58610     getIndexById : function(id){
58611         for(var i = 0, len = this.config.length; i < len; i++){
58612             if(this.config[i].id == id){
58613                 return i;
58614             }
58615         }
58616         return -1;
58617     },
58618     
58619     /**
58620      * Returns the index for a specified column dataIndex.
58621      * @param {String} dataIndex The column dataIndex
58622      * @return {Number} the index, or -1 if not found
58623      */
58624     
58625     findColumnIndex : function(dataIndex){
58626         for(var i = 0, len = this.config.length; i < len; i++){
58627             if(this.config[i].dataIndex == dataIndex){
58628                 return i;
58629             }
58630         }
58631         return -1;
58632     },
58633     
58634     
58635     moveColumn : function(oldIndex, newIndex){
58636         var c = this.config[oldIndex];
58637         this.config.splice(oldIndex, 1);
58638         this.config.splice(newIndex, 0, c);
58639         this.dataMap = null;
58640         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58641     },
58642
58643     isLocked : function(colIndex){
58644         return this.config[colIndex].locked === true;
58645     },
58646
58647     setLocked : function(colIndex, value, suppressEvent){
58648         if(this.isLocked(colIndex) == value){
58649             return;
58650         }
58651         this.config[colIndex].locked = value;
58652         if(!suppressEvent){
58653             this.fireEvent("columnlockchange", this, colIndex, value);
58654         }
58655     },
58656
58657     getTotalLockedWidth : function(){
58658         var totalWidth = 0;
58659         for(var i = 0; i < this.config.length; i++){
58660             if(this.isLocked(i) && !this.isHidden(i)){
58661                 this.totalWidth += this.getColumnWidth(i);
58662             }
58663         }
58664         return totalWidth;
58665     },
58666
58667     getLockedCount : function(){
58668         for(var i = 0, len = this.config.length; i < len; i++){
58669             if(!this.isLocked(i)){
58670                 return i;
58671             }
58672         }
58673         
58674         return this.config.length;
58675     },
58676
58677     /**
58678      * Returns the number of columns.
58679      * @return {Number}
58680      */
58681     getColumnCount : function(visibleOnly){
58682         if(visibleOnly === true){
58683             var c = 0;
58684             for(var i = 0, len = this.config.length; i < len; i++){
58685                 if(!this.isHidden(i)){
58686                     c++;
58687                 }
58688             }
58689             return c;
58690         }
58691         return this.config.length;
58692     },
58693
58694     /**
58695      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58696      * @param {Function} fn
58697      * @param {Object} scope (optional)
58698      * @return {Array} result
58699      */
58700     getColumnsBy : function(fn, scope){
58701         var r = [];
58702         for(var i = 0, len = this.config.length; i < len; i++){
58703             var c = this.config[i];
58704             if(fn.call(scope||this, c, i) === true){
58705                 r[r.length] = c;
58706             }
58707         }
58708         return r;
58709     },
58710
58711     /**
58712      * Returns true if the specified column is sortable.
58713      * @param {Number} col The column index
58714      * @return {Boolean}
58715      */
58716     isSortable : function(col){
58717         if(typeof this.config[col].sortable == "undefined"){
58718             return this.defaultSortable;
58719         }
58720         return this.config[col].sortable;
58721     },
58722
58723     /**
58724      * Returns the rendering (formatting) function defined for the column.
58725      * @param {Number} col The column index.
58726      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58727      */
58728     getRenderer : function(col){
58729         if(!this.config[col].renderer){
58730             return Roo.grid.ColumnModel.defaultRenderer;
58731         }
58732         return this.config[col].renderer;
58733     },
58734
58735     /**
58736      * Sets the rendering (formatting) function for a column.
58737      * @param {Number} col The column index
58738      * @param {Function} fn The function to use to process the cell's raw data
58739      * to return HTML markup for the grid view. The render function is called with
58740      * the following parameters:<ul>
58741      * <li>Data value.</li>
58742      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58743      * <li>css A CSS style string to apply to the table cell.</li>
58744      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58745      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58746      * <li>Row index</li>
58747      * <li>Column index</li>
58748      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58749      */
58750     setRenderer : function(col, fn){
58751         this.config[col].renderer = fn;
58752     },
58753
58754     /**
58755      * Returns the width for the specified column.
58756      * @param {Number} col The column index
58757      * @param (optional) {String} gridSize bootstrap width size.
58758      * @return {Number}
58759      */
58760     getColumnWidth : function(col, gridSize)
58761         {
58762                 var cfg = this.config[col];
58763                 
58764                 if (typeof(gridSize) == 'undefined') {
58765                         return cfg.width * 1 || this.defaultWidth;
58766                 }
58767                 if (gridSize === false) { // if we set it..
58768                         return cfg.width || false;
58769                 }
58770                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58771                 
58772                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58773                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58774                                 continue;
58775                         }
58776                         return cfg[ sizes[i] ];
58777                 }
58778                 return 1;
58779                 
58780     },
58781
58782     /**
58783      * Sets the width for a column.
58784      * @param {Number} col The column index
58785      * @param {Number} width The new width
58786      */
58787     setColumnWidth : function(col, width, suppressEvent){
58788         this.config[col].width = width;
58789         this.totalWidth = null;
58790         if(!suppressEvent){
58791              this.fireEvent("widthchange", this, col, width);
58792         }
58793     },
58794
58795     /**
58796      * Returns the total width of all columns.
58797      * @param {Boolean} includeHidden True to include hidden column widths
58798      * @return {Number}
58799      */
58800     getTotalWidth : function(includeHidden){
58801         if(!this.totalWidth){
58802             this.totalWidth = 0;
58803             for(var i = 0, len = this.config.length; i < len; i++){
58804                 if(includeHidden || !this.isHidden(i)){
58805                     this.totalWidth += this.getColumnWidth(i);
58806                 }
58807             }
58808         }
58809         return this.totalWidth;
58810     },
58811
58812     /**
58813      * Returns the header for the specified column.
58814      * @param {Number} col The column index
58815      * @return {String}
58816      */
58817     getColumnHeader : function(col){
58818         return this.config[col].header;
58819     },
58820
58821     /**
58822      * Sets the header for a column.
58823      * @param {Number} col The column index
58824      * @param {String} header The new header
58825      */
58826     setColumnHeader : function(col, header){
58827         this.config[col].header = header;
58828         this.fireEvent("headerchange", this, col, header);
58829     },
58830
58831     /**
58832      * Returns the tooltip for the specified column.
58833      * @param {Number} col The column index
58834      * @return {String}
58835      */
58836     getColumnTooltip : function(col){
58837             return this.config[col].tooltip;
58838     },
58839     /**
58840      * Sets the tooltip for a column.
58841      * @param {Number} col The column index
58842      * @param {String} tooltip The new tooltip
58843      */
58844     setColumnTooltip : function(col, tooltip){
58845             this.config[col].tooltip = tooltip;
58846     },
58847
58848     /**
58849      * Returns the dataIndex for the specified column.
58850      * @param {Number} col The column index
58851      * @return {Number}
58852      */
58853     getDataIndex : function(col){
58854         return this.config[col].dataIndex;
58855     },
58856
58857     /**
58858      * Sets the dataIndex for a column.
58859      * @param {Number} col The column index
58860      * @param {Number} dataIndex The new dataIndex
58861      */
58862     setDataIndex : function(col, dataIndex){
58863         this.config[col].dataIndex = dataIndex;
58864     },
58865
58866     
58867     
58868     /**
58869      * Returns true if the cell is editable.
58870      * @param {Number} colIndex The column index
58871      * @param {Number} rowIndex The row index - this is nto actually used..?
58872      * @return {Boolean}
58873      */
58874     isCellEditable : function(colIndex, rowIndex){
58875         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58876     },
58877
58878     /**
58879      * Returns the editor defined for the cell/column.
58880      * return false or null to disable editing.
58881      * @param {Number} colIndex The column index
58882      * @param {Number} rowIndex The row index
58883      * @return {Object}
58884      */
58885     getCellEditor : function(colIndex, rowIndex){
58886         return this.config[colIndex].editor;
58887     },
58888
58889     /**
58890      * Sets if a column is editable.
58891      * @param {Number} col The column index
58892      * @param {Boolean} editable True if the column is editable
58893      */
58894     setEditable : function(col, editable){
58895         this.config[col].editable = editable;
58896     },
58897
58898
58899     /**
58900      * Returns true if the column is hidden.
58901      * @param {Number} colIndex The column index
58902      * @return {Boolean}
58903      */
58904     isHidden : function(colIndex){
58905         return this.config[colIndex].hidden;
58906     },
58907
58908
58909     /**
58910      * Returns true if the column width cannot be changed
58911      */
58912     isFixed : function(colIndex){
58913         return this.config[colIndex].fixed;
58914     },
58915
58916     /**
58917      * Returns true if the column can be resized
58918      * @return {Boolean}
58919      */
58920     isResizable : function(colIndex){
58921         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58922     },
58923     /**
58924      * Sets if a column is hidden.
58925      * @param {Number} colIndex The column index
58926      * @param {Boolean} hidden True if the column is hidden
58927      */
58928     setHidden : function(colIndex, hidden){
58929         this.config[colIndex].hidden = hidden;
58930         this.totalWidth = null;
58931         this.fireEvent("hiddenchange", this, colIndex, hidden);
58932     },
58933
58934     /**
58935      * Sets the editor for a column.
58936      * @param {Number} col The column index
58937      * @param {Object} editor The editor object
58938      */
58939     setEditor : function(col, editor){
58940         this.config[col].editor = editor;
58941     },
58942     /**
58943      * Add a column (experimental...) - defaults to adding to the end..
58944      * @param {Object} config 
58945     */
58946     addColumn : function(c)
58947     {
58948     
58949         var i = this.config.length;
58950         this.config[i] = c;
58951         
58952         if(typeof c.dataIndex == "undefined"){
58953             c.dataIndex = i;
58954         }
58955         if(typeof c.renderer == "string"){
58956             c.renderer = Roo.util.Format[c.renderer];
58957         }
58958         if(typeof c.id == "undefined"){
58959             c.id = Roo.id();
58960         }
58961         if(c.editor && c.editor.xtype){
58962             c.editor  = Roo.factory(c.editor, Roo.grid);
58963         }
58964         if(c.editor && c.editor.isFormField){
58965             c.editor = new Roo.grid.GridEditor(c.editor);
58966         }
58967         this.lookup[c.id] = c;
58968     }
58969     
58970 });
58971
58972 Roo.grid.ColumnModel.defaultRenderer = function(value)
58973 {
58974     if(typeof value == "object") {
58975         return value;
58976     }
58977         if(typeof value == "string" && value.length < 1){
58978             return "&#160;";
58979         }
58980     
58981         return String.format("{0}", value);
58982 };
58983
58984 // Alias for backwards compatibility
58985 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58986 /*
58987  * Based on:
58988  * Ext JS Library 1.1.1
58989  * Copyright(c) 2006-2007, Ext JS, LLC.
58990  *
58991  * Originally Released Under LGPL - original licence link has changed is not relivant.
58992  *
58993  * Fork - LGPL
58994  * <script type="text/javascript">
58995  */
58996
58997 /**
58998  * @class Roo.grid.AbstractSelectionModel
58999  * @extends Roo.util.Observable
59000  * @abstract
59001  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59002  * implemented by descendant classes.  This class should not be directly instantiated.
59003  * @constructor
59004  */
59005 Roo.grid.AbstractSelectionModel = function(){
59006     this.locked = false;
59007     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59008 };
59009
59010 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59011     /** @ignore Called by the grid automatically. Do not call directly. */
59012     init : function(grid){
59013         this.grid = grid;
59014         this.initEvents();
59015     },
59016
59017     /**
59018      * Locks the selections.
59019      */
59020     lock : function(){
59021         this.locked = true;
59022     },
59023
59024     /**
59025      * Unlocks the selections.
59026      */
59027     unlock : function(){
59028         this.locked = false;
59029     },
59030
59031     /**
59032      * Returns true if the selections are locked.
59033      * @return {Boolean}
59034      */
59035     isLocked : function(){
59036         return this.locked;
59037     }
59038 });/*
59039  * Based on:
59040  * Ext JS Library 1.1.1
59041  * Copyright(c) 2006-2007, Ext JS, LLC.
59042  *
59043  * Originally Released Under LGPL - original licence link has changed is not relivant.
59044  *
59045  * Fork - LGPL
59046  * <script type="text/javascript">
59047  */
59048 /**
59049  * @extends Roo.grid.AbstractSelectionModel
59050  * @class Roo.grid.RowSelectionModel
59051  * The default SelectionModel used by {@link Roo.grid.Grid}.
59052  * It supports multiple selections and keyboard selection/navigation. 
59053  * @constructor
59054  * @param {Object} config
59055  */
59056 Roo.grid.RowSelectionModel = function(config){
59057     Roo.apply(this, config);
59058     this.selections = new Roo.util.MixedCollection(false, function(o){
59059         return o.id;
59060     });
59061
59062     this.last = false;
59063     this.lastActive = false;
59064
59065     this.addEvents({
59066         /**
59067         * @event selectionchange
59068         * Fires when the selection changes
59069         * @param {SelectionModel} this
59070         */
59071        "selectionchange" : true,
59072        /**
59073         * @event afterselectionchange
59074         * Fires after the selection changes (eg. by key press or clicking)
59075         * @param {SelectionModel} this
59076         */
59077        "afterselectionchange" : true,
59078        /**
59079         * @event beforerowselect
59080         * Fires when a row is selected being selected, return false to cancel.
59081         * @param {SelectionModel} this
59082         * @param {Number} rowIndex The selected index
59083         * @param {Boolean} keepExisting False if other selections will be cleared
59084         */
59085        "beforerowselect" : true,
59086        /**
59087         * @event rowselect
59088         * Fires when a row is selected.
59089         * @param {SelectionModel} this
59090         * @param {Number} rowIndex The selected index
59091         * @param {Roo.data.Record} r The record
59092         */
59093        "rowselect" : true,
59094        /**
59095         * @event rowdeselect
59096         * Fires when a row is deselected.
59097         * @param {SelectionModel} this
59098         * @param {Number} rowIndex The selected index
59099         */
59100         "rowdeselect" : true
59101     });
59102     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59103     this.locked = false;
59104 };
59105
59106 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59107     /**
59108      * @cfg {Boolean} singleSelect
59109      * True to allow selection of only one row at a time (defaults to false)
59110      */
59111     singleSelect : false,
59112
59113     // private
59114     initEvents : function(){
59115
59116         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59117             this.grid.on("mousedown", this.handleMouseDown, this);
59118         }else{ // allow click to work like normal
59119             this.grid.on("rowclick", this.handleDragableRowClick, this);
59120         }
59121         // bootstrap does not have a view..
59122         var view = this.grid.view ? this.grid.view : this.grid;
59123         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59124             "up" : function(e){
59125                 if(!e.shiftKey){
59126                     this.selectPrevious(e.shiftKey);
59127                 }else if(this.last !== false && this.lastActive !== false){
59128                     var last = this.last;
59129                     this.selectRange(this.last,  this.lastActive-1);
59130                     view.focusRow(this.lastActive);
59131                     if(last !== false){
59132                         this.last = last;
59133                     }
59134                 }else{
59135                     this.selectFirstRow();
59136                 }
59137                 this.fireEvent("afterselectionchange", this);
59138             },
59139             "down" : function(e){
59140                 if(!e.shiftKey){
59141                     this.selectNext(e.shiftKey);
59142                 }else if(this.last !== false && this.lastActive !== false){
59143                     var last = this.last;
59144                     this.selectRange(this.last,  this.lastActive+1);
59145                     view.focusRow(this.lastActive);
59146                     if(last !== false){
59147                         this.last = last;
59148                     }
59149                 }else{
59150                     this.selectFirstRow();
59151                 }
59152                 this.fireEvent("afterselectionchange", this);
59153             },
59154             scope: this
59155         });
59156
59157          
59158         view.on("refresh", this.onRefresh, this);
59159         view.on("rowupdated", this.onRowUpdated, this);
59160         view.on("rowremoved", this.onRemove, this);
59161     },
59162
59163     // private
59164     onRefresh : function(){
59165         var ds = this.grid.ds, i, v = this.grid.view;
59166         var s = this.selections;
59167         s.each(function(r){
59168             if((i = ds.indexOfId(r.id)) != -1){
59169                 v.onRowSelect(i);
59170                 s.add(ds.getAt(i)); // updating the selection relate data
59171             }else{
59172                 s.remove(r);
59173             }
59174         });
59175     },
59176
59177     // private
59178     onRemove : function(v, index, r){
59179         this.selections.remove(r);
59180     },
59181
59182     // private
59183     onRowUpdated : function(v, index, r){
59184         if(this.isSelected(r)){
59185             v.onRowSelect(index);
59186         }
59187     },
59188
59189     /**
59190      * Select records.
59191      * @param {Array} records The records to select
59192      * @param {Boolean} keepExisting (optional) True to keep existing selections
59193      */
59194     selectRecords : function(records, keepExisting){
59195         if(!keepExisting){
59196             this.clearSelections();
59197         }
59198         var ds = this.grid.ds;
59199         for(var i = 0, len = records.length; i < len; i++){
59200             this.selectRow(ds.indexOf(records[i]), true);
59201         }
59202     },
59203
59204     /**
59205      * Gets the number of selected rows.
59206      * @return {Number}
59207      */
59208     getCount : function(){
59209         return this.selections.length;
59210     },
59211
59212     /**
59213      * Selects the first row in the grid.
59214      */
59215     selectFirstRow : function(){
59216         this.selectRow(0);
59217     },
59218
59219     /**
59220      * Select the last row.
59221      * @param {Boolean} keepExisting (optional) True to keep existing selections
59222      */
59223     selectLastRow : function(keepExisting){
59224         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59225     },
59226
59227     /**
59228      * Selects the row immediately following the last selected row.
59229      * @param {Boolean} keepExisting (optional) True to keep existing selections
59230      */
59231     selectNext : function(keepExisting){
59232         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59233             this.selectRow(this.last+1, keepExisting);
59234             var view = this.grid.view ? this.grid.view : this.grid;
59235             view.focusRow(this.last);
59236         }
59237     },
59238
59239     /**
59240      * Selects the row that precedes the last selected row.
59241      * @param {Boolean} keepExisting (optional) True to keep existing selections
59242      */
59243     selectPrevious : function(keepExisting){
59244         if(this.last){
59245             this.selectRow(this.last-1, keepExisting);
59246             var view = this.grid.view ? this.grid.view : this.grid;
59247             view.focusRow(this.last);
59248         }
59249     },
59250
59251     /**
59252      * Returns the selected records
59253      * @return {Array} Array of selected records
59254      */
59255     getSelections : function(){
59256         return [].concat(this.selections.items);
59257     },
59258
59259     /**
59260      * Returns the first selected record.
59261      * @return {Record}
59262      */
59263     getSelected : function(){
59264         return this.selections.itemAt(0);
59265     },
59266
59267
59268     /**
59269      * Clears all selections.
59270      */
59271     clearSelections : function(fast){
59272         if(this.locked) {
59273             return;
59274         }
59275         if(fast !== true){
59276             var ds = this.grid.ds;
59277             var s = this.selections;
59278             s.each(function(r){
59279                 this.deselectRow(ds.indexOfId(r.id));
59280             }, this);
59281             s.clear();
59282         }else{
59283             this.selections.clear();
59284         }
59285         this.last = false;
59286     },
59287
59288
59289     /**
59290      * Selects all rows.
59291      */
59292     selectAll : function(){
59293         if(this.locked) {
59294             return;
59295         }
59296         this.selections.clear();
59297         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59298             this.selectRow(i, true);
59299         }
59300     },
59301
59302     /**
59303      * Returns True if there is a selection.
59304      * @return {Boolean}
59305      */
59306     hasSelection : function(){
59307         return this.selections.length > 0;
59308     },
59309
59310     /**
59311      * Returns True if the specified row is selected.
59312      * @param {Number/Record} record The record or index of the record to check
59313      * @return {Boolean}
59314      */
59315     isSelected : function(index){
59316         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59317         return (r && this.selections.key(r.id) ? true : false);
59318     },
59319
59320     /**
59321      * Returns True if the specified record id is selected.
59322      * @param {String} id The id of record to check
59323      * @return {Boolean}
59324      */
59325     isIdSelected : function(id){
59326         return (this.selections.key(id) ? true : false);
59327     },
59328
59329     // private
59330     handleMouseDown : function(e, t)
59331     {
59332         var view = this.grid.view ? this.grid.view : this.grid;
59333         var rowIndex;
59334         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59335             return;
59336         };
59337         if(e.shiftKey && this.last !== false){
59338             var last = this.last;
59339             this.selectRange(last, rowIndex, e.ctrlKey);
59340             this.last = last; // reset the last
59341             view.focusRow(rowIndex);
59342         }else{
59343             var isSelected = this.isSelected(rowIndex);
59344             if(e.button !== 0 && isSelected){
59345                 view.focusRow(rowIndex);
59346             }else if(e.ctrlKey && isSelected){
59347                 this.deselectRow(rowIndex);
59348             }else if(!isSelected){
59349                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59350                 view.focusRow(rowIndex);
59351             }
59352         }
59353         this.fireEvent("afterselectionchange", this);
59354     },
59355     // private
59356     handleDragableRowClick :  function(grid, rowIndex, e) 
59357     {
59358         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59359             this.selectRow(rowIndex, false);
59360             var view = this.grid.view ? this.grid.view : this.grid;
59361             view.focusRow(rowIndex);
59362              this.fireEvent("afterselectionchange", this);
59363         }
59364     },
59365     
59366     /**
59367      * Selects multiple rows.
59368      * @param {Array} rows Array of the indexes of the row to select
59369      * @param {Boolean} keepExisting (optional) True to keep existing selections
59370      */
59371     selectRows : function(rows, keepExisting){
59372         if(!keepExisting){
59373             this.clearSelections();
59374         }
59375         for(var i = 0, len = rows.length; i < len; i++){
59376             this.selectRow(rows[i], true);
59377         }
59378     },
59379
59380     /**
59381      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59382      * @param {Number} startRow The index of the first row in the range
59383      * @param {Number} endRow The index of the last row in the range
59384      * @param {Boolean} keepExisting (optional) True to retain existing selections
59385      */
59386     selectRange : function(startRow, endRow, keepExisting){
59387         if(this.locked) {
59388             return;
59389         }
59390         if(!keepExisting){
59391             this.clearSelections();
59392         }
59393         if(startRow <= endRow){
59394             for(var i = startRow; i <= endRow; i++){
59395                 this.selectRow(i, true);
59396             }
59397         }else{
59398             for(var i = startRow; i >= endRow; i--){
59399                 this.selectRow(i, true);
59400             }
59401         }
59402     },
59403
59404     /**
59405      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59406      * @param {Number} startRow The index of the first row in the range
59407      * @param {Number} endRow The index of the last row in the range
59408      */
59409     deselectRange : function(startRow, endRow, preventViewNotify){
59410         if(this.locked) {
59411             return;
59412         }
59413         for(var i = startRow; i <= endRow; i++){
59414             this.deselectRow(i, preventViewNotify);
59415         }
59416     },
59417
59418     /**
59419      * Selects a row.
59420      * @param {Number} row The index of the row to select
59421      * @param {Boolean} keepExisting (optional) True to keep existing selections
59422      */
59423     selectRow : function(index, keepExisting, preventViewNotify){
59424         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59425             return;
59426         }
59427         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59428             if(!keepExisting || this.singleSelect){
59429                 this.clearSelections();
59430             }
59431             var r = this.grid.ds.getAt(index);
59432             this.selections.add(r);
59433             this.last = this.lastActive = index;
59434             if(!preventViewNotify){
59435                 var view = this.grid.view ? this.grid.view : this.grid;
59436                 view.onRowSelect(index);
59437             }
59438             this.fireEvent("rowselect", this, index, r);
59439             this.fireEvent("selectionchange", this);
59440         }
59441     },
59442
59443     /**
59444      * Deselects a row.
59445      * @param {Number} row The index of the row to deselect
59446      */
59447     deselectRow : function(index, preventViewNotify){
59448         if(this.locked) {
59449             return;
59450         }
59451         if(this.last == index){
59452             this.last = false;
59453         }
59454         if(this.lastActive == index){
59455             this.lastActive = false;
59456         }
59457         var r = this.grid.ds.getAt(index);
59458         this.selections.remove(r);
59459         if(!preventViewNotify){
59460             var view = this.grid.view ? this.grid.view : this.grid;
59461             view.onRowDeselect(index);
59462         }
59463         this.fireEvent("rowdeselect", this, index);
59464         this.fireEvent("selectionchange", this);
59465     },
59466
59467     // private
59468     restoreLast : function(){
59469         if(this._last){
59470             this.last = this._last;
59471         }
59472     },
59473
59474     // private
59475     acceptsNav : function(row, col, cm){
59476         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59477     },
59478
59479     // private
59480     onEditorKey : function(field, e){
59481         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59482         if(k == e.TAB){
59483             e.stopEvent();
59484             ed.completeEdit();
59485             if(e.shiftKey){
59486                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59487             }else{
59488                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59489             }
59490         }else if(k == e.ENTER && !e.ctrlKey){
59491             e.stopEvent();
59492             ed.completeEdit();
59493             if(e.shiftKey){
59494                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59495             }else{
59496                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59497             }
59498         }else if(k == e.ESC){
59499             ed.cancelEdit();
59500         }
59501         if(newCell){
59502             g.startEditing(newCell[0], newCell[1]);
59503         }
59504     }
59505 });/*
59506  * Based on:
59507  * Ext JS Library 1.1.1
59508  * Copyright(c) 2006-2007, Ext JS, LLC.
59509  *
59510  * Originally Released Under LGPL - original licence link has changed is not relivant.
59511  *
59512  * Fork - LGPL
59513  * <script type="text/javascript">
59514  */
59515 /**
59516  * @class Roo.grid.CellSelectionModel
59517  * @extends Roo.grid.AbstractSelectionModel
59518  * This class provides the basic implementation for cell selection in a grid.
59519  * @constructor
59520  * @param {Object} config The object containing the configuration of this model.
59521  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59522  */
59523 Roo.grid.CellSelectionModel = function(config){
59524     Roo.apply(this, config);
59525
59526     this.selection = null;
59527
59528     this.addEvents({
59529         /**
59530              * @event beforerowselect
59531              * Fires before a cell is selected.
59532              * @param {SelectionModel} this
59533              * @param {Number} rowIndex The selected row index
59534              * @param {Number} colIndex The selected cell index
59535              */
59536             "beforecellselect" : true,
59537         /**
59538              * @event cellselect
59539              * Fires when a cell is selected.
59540              * @param {SelectionModel} this
59541              * @param {Number} rowIndex The selected row index
59542              * @param {Number} colIndex The selected cell index
59543              */
59544             "cellselect" : true,
59545         /**
59546              * @event selectionchange
59547              * Fires when the active selection changes.
59548              * @param {SelectionModel} this
59549              * @param {Object} selection null for no selection or an object (o) with two properties
59550                 <ul>
59551                 <li>o.record: the record object for the row the selection is in</li>
59552                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59553                 </ul>
59554              */
59555             "selectionchange" : true,
59556         /**
59557              * @event tabend
59558              * Fires when the tab (or enter) was pressed on the last editable cell
59559              * You can use this to trigger add new row.
59560              * @param {SelectionModel} this
59561              */
59562             "tabend" : true,
59563          /**
59564              * @event beforeeditnext
59565              * Fires before the next editable sell is made active
59566              * You can use this to skip to another cell or fire the tabend
59567              *    if you set cell to false
59568              * @param {Object} eventdata object : { cell : [ row, col ] } 
59569              */
59570             "beforeeditnext" : true
59571     });
59572     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59573 };
59574
59575 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59576     
59577     enter_is_tab: false,
59578
59579     /** @ignore */
59580     initEvents : function(){
59581         this.grid.on("mousedown", this.handleMouseDown, this);
59582         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59583         var view = this.grid.view;
59584         view.on("refresh", this.onViewChange, this);
59585         view.on("rowupdated", this.onRowUpdated, this);
59586         view.on("beforerowremoved", this.clearSelections, this);
59587         view.on("beforerowsinserted", this.clearSelections, this);
59588         if(this.grid.isEditor){
59589             this.grid.on("beforeedit", this.beforeEdit,  this);
59590         }
59591     },
59592
59593         //private
59594     beforeEdit : function(e){
59595         this.select(e.row, e.column, false, true, e.record);
59596     },
59597
59598         //private
59599     onRowUpdated : function(v, index, r){
59600         if(this.selection && this.selection.record == r){
59601             v.onCellSelect(index, this.selection.cell[1]);
59602         }
59603     },
59604
59605         //private
59606     onViewChange : function(){
59607         this.clearSelections(true);
59608     },
59609
59610         /**
59611          * Returns the currently selected cell,.
59612          * @return {Array} The selected cell (row, column) or null if none selected.
59613          */
59614     getSelectedCell : function(){
59615         return this.selection ? this.selection.cell : null;
59616     },
59617
59618     /**
59619      * Clears all selections.
59620      * @param {Boolean} true to prevent the gridview from being notified about the change.
59621      */
59622     clearSelections : function(preventNotify){
59623         var s = this.selection;
59624         if(s){
59625             if(preventNotify !== true){
59626                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59627             }
59628             this.selection = null;
59629             this.fireEvent("selectionchange", this, null);
59630         }
59631     },
59632
59633     /**
59634      * Returns true if there is a selection.
59635      * @return {Boolean}
59636      */
59637     hasSelection : function(){
59638         return this.selection ? true : false;
59639     },
59640
59641     /** @ignore */
59642     handleMouseDown : function(e, t){
59643         var v = this.grid.getView();
59644         if(this.isLocked()){
59645             return;
59646         };
59647         var row = v.findRowIndex(t);
59648         var cell = v.findCellIndex(t);
59649         if(row !== false && cell !== false){
59650             this.select(row, cell);
59651         }
59652     },
59653
59654     /**
59655      * Selects a cell.
59656      * @param {Number} rowIndex
59657      * @param {Number} collIndex
59658      */
59659     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59660         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59661             this.clearSelections();
59662             r = r || this.grid.dataSource.getAt(rowIndex);
59663             this.selection = {
59664                 record : r,
59665                 cell : [rowIndex, colIndex]
59666             };
59667             if(!preventViewNotify){
59668                 var v = this.grid.getView();
59669                 v.onCellSelect(rowIndex, colIndex);
59670                 if(preventFocus !== true){
59671                     v.focusCell(rowIndex, colIndex);
59672                 }
59673             }
59674             this.fireEvent("cellselect", this, rowIndex, colIndex);
59675             this.fireEvent("selectionchange", this, this.selection);
59676         }
59677     },
59678
59679         //private
59680     isSelectable : function(rowIndex, colIndex, cm){
59681         return !cm.isHidden(colIndex);
59682     },
59683
59684     /** @ignore */
59685     handleKeyDown : function(e){
59686         //Roo.log('Cell Sel Model handleKeyDown');
59687         if(!e.isNavKeyPress()){
59688             return;
59689         }
59690         var g = this.grid, s = this.selection;
59691         if(!s){
59692             e.stopEvent();
59693             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59694             if(cell){
59695                 this.select(cell[0], cell[1]);
59696             }
59697             return;
59698         }
59699         var sm = this;
59700         var walk = function(row, col, step){
59701             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59702         };
59703         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59704         var newCell;
59705
59706       
59707
59708         switch(k){
59709             case e.TAB:
59710                 // handled by onEditorKey
59711                 if (g.isEditor && g.editing) {
59712                     return;
59713                 }
59714                 if(e.shiftKey) {
59715                     newCell = walk(r, c-1, -1);
59716                 } else {
59717                     newCell = walk(r, c+1, 1);
59718                 }
59719                 break;
59720             
59721             case e.DOWN:
59722                newCell = walk(r+1, c, 1);
59723                 break;
59724             
59725             case e.UP:
59726                 newCell = walk(r-1, c, -1);
59727                 break;
59728             
59729             case e.RIGHT:
59730                 newCell = walk(r, c+1, 1);
59731                 break;
59732             
59733             case e.LEFT:
59734                 newCell = walk(r, c-1, -1);
59735                 break;
59736             
59737             case e.ENTER:
59738                 
59739                 if(g.isEditor && !g.editing){
59740                    g.startEditing(r, c);
59741                    e.stopEvent();
59742                    return;
59743                 }
59744                 
59745                 
59746              break;
59747         };
59748         if(newCell){
59749             this.select(newCell[0], newCell[1]);
59750             e.stopEvent();
59751             
59752         }
59753     },
59754
59755     acceptsNav : function(row, col, cm){
59756         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59757     },
59758     /**
59759      * Selects a cell.
59760      * @param {Number} field (not used) - as it's normally used as a listener
59761      * @param {Number} e - event - fake it by using
59762      *
59763      * var e = Roo.EventObjectImpl.prototype;
59764      * e.keyCode = e.TAB
59765      *
59766      * 
59767      */
59768     onEditorKey : function(field, e){
59769         
59770         var k = e.getKey(),
59771             newCell,
59772             g = this.grid,
59773             ed = g.activeEditor,
59774             forward = false;
59775         ///Roo.log('onEditorKey' + k);
59776         
59777         
59778         if (this.enter_is_tab && k == e.ENTER) {
59779             k = e.TAB;
59780         }
59781         
59782         if(k == e.TAB){
59783             if(e.shiftKey){
59784                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59785             }else{
59786                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59787                 forward = true;
59788             }
59789             
59790             e.stopEvent();
59791             
59792         } else if(k == e.ENTER &&  !e.ctrlKey){
59793             ed.completeEdit();
59794             e.stopEvent();
59795             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59796         
59797                 } else if(k == e.ESC){
59798             ed.cancelEdit();
59799         }
59800                 
59801         if (newCell) {
59802             var ecall = { cell : newCell, forward : forward };
59803             this.fireEvent('beforeeditnext', ecall );
59804             newCell = ecall.cell;
59805                         forward = ecall.forward;
59806         }
59807                 
59808         if(newCell){
59809             //Roo.log('next cell after edit');
59810             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59811         } else if (forward) {
59812             // tabbed past last
59813             this.fireEvent.defer(100, this, ['tabend',this]);
59814         }
59815     }
59816 });/*
59817  * Based on:
59818  * Ext JS Library 1.1.1
59819  * Copyright(c) 2006-2007, Ext JS, LLC.
59820  *
59821  * Originally Released Under LGPL - original licence link has changed is not relivant.
59822  *
59823  * Fork - LGPL
59824  * <script type="text/javascript">
59825  */
59826  
59827 /**
59828  * @class Roo.grid.EditorGrid
59829  * @extends Roo.grid.Grid
59830  * Class for creating and editable grid.
59831  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59832  * The container MUST have some type of size defined for the grid to fill. The container will be 
59833  * automatically set to position relative if it isn't already.
59834  * @param {Object} dataSource The data model to bind to
59835  * @param {Object} colModel The column model with info about this grid's columns
59836  */
59837 Roo.grid.EditorGrid = function(container, config){
59838     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59839     this.getGridEl().addClass("xedit-grid");
59840
59841     if(!this.selModel){
59842         this.selModel = new Roo.grid.CellSelectionModel();
59843     }
59844
59845     this.activeEditor = null;
59846
59847         this.addEvents({
59848             /**
59849              * @event beforeedit
59850              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59851              * <ul style="padding:5px;padding-left:16px;">
59852              * <li>grid - This grid</li>
59853              * <li>record - The record being edited</li>
59854              * <li>field - The field name being edited</li>
59855              * <li>value - The value for the field being edited.</li>
59856              * <li>row - The grid row index</li>
59857              * <li>column - The grid column index</li>
59858              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59859              * </ul>
59860              * @param {Object} e An edit event (see above for description)
59861              */
59862             "beforeedit" : true,
59863             /**
59864              * @event afteredit
59865              * Fires after a cell is edited. <br />
59866              * <ul style="padding:5px;padding-left:16px;">
59867              * <li>grid - This grid</li>
59868              * <li>record - The record being edited</li>
59869              * <li>field - The field name being edited</li>
59870              * <li>value - The value being set</li>
59871              * <li>originalValue - The original value for the field, before the edit.</li>
59872              * <li>row - The grid row index</li>
59873              * <li>column - The grid column index</li>
59874              * </ul>
59875              * @param {Object} e An edit event (see above for description)
59876              */
59877             "afteredit" : true,
59878             /**
59879              * @event validateedit
59880              * Fires after a cell is edited, but before the value is set in the record. 
59881          * You can use this to modify the value being set in the field, Return false
59882              * to cancel the change. The edit event object has the following properties <br />
59883              * <ul style="padding:5px;padding-left:16px;">
59884          * <li>editor - This editor</li>
59885              * <li>grid - This grid</li>
59886              * <li>record - The record being edited</li>
59887              * <li>field - The field name being edited</li>
59888              * <li>value - The value being set</li>
59889              * <li>originalValue - The original value for the field, before the edit.</li>
59890              * <li>row - The grid row index</li>
59891              * <li>column - The grid column index</li>
59892              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59893              * </ul>
59894              * @param {Object} e An edit event (see above for description)
59895              */
59896             "validateedit" : true
59897         });
59898     this.on("bodyscroll", this.stopEditing,  this);
59899     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59900 };
59901
59902 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59903     /**
59904      * @cfg {Number} clicksToEdit
59905      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59906      */
59907     clicksToEdit: 2,
59908
59909     // private
59910     isEditor : true,
59911     // private
59912     trackMouseOver: false, // causes very odd FF errors
59913
59914     onCellDblClick : function(g, row, col){
59915         this.startEditing(row, col);
59916     },
59917
59918     onEditComplete : function(ed, value, startValue){
59919         this.editing = false;
59920         this.activeEditor = null;
59921         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59922         var r = ed.record;
59923         var field = this.colModel.getDataIndex(ed.col);
59924         var e = {
59925             grid: this,
59926             record: r,
59927             field: field,
59928             originalValue: startValue,
59929             value: value,
59930             row: ed.row,
59931             column: ed.col,
59932             cancel:false,
59933             editor: ed
59934         };
59935         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59936         cell.show();
59937           
59938         if(String(value) !== String(startValue)){
59939             
59940             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59941                 r.set(field, e.value);
59942                 // if we are dealing with a combo box..
59943                 // then we also set the 'name' colum to be the displayField
59944                 if (ed.field.displayField && ed.field.name) {
59945                     r.set(ed.field.name, ed.field.el.dom.value);
59946                 }
59947                 
59948                 delete e.cancel; //?? why!!!
59949                 this.fireEvent("afteredit", e);
59950             }
59951         } else {
59952             this.fireEvent("afteredit", e); // always fire it!
59953         }
59954         this.view.focusCell(ed.row, ed.col);
59955     },
59956
59957     /**
59958      * Starts editing the specified for the specified row/column
59959      * @param {Number} rowIndex
59960      * @param {Number} colIndex
59961      */
59962     startEditing : function(row, col){
59963         this.stopEditing();
59964         if(this.colModel.isCellEditable(col, row)){
59965             this.view.ensureVisible(row, col, true);
59966           
59967             var r = this.dataSource.getAt(row);
59968             var field = this.colModel.getDataIndex(col);
59969             var cell = Roo.get(this.view.getCell(row,col));
59970             var e = {
59971                 grid: this,
59972                 record: r,
59973                 field: field,
59974                 value: r.data[field],
59975                 row: row,
59976                 column: col,
59977                 cancel:false 
59978             };
59979             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59980                 this.editing = true;
59981                 var ed = this.colModel.getCellEditor(col, row);
59982                 
59983                 if (!ed) {
59984                     return;
59985                 }
59986                 if(!ed.rendered){
59987                     ed.render(ed.parentEl || document.body);
59988                 }
59989                 ed.field.reset();
59990                
59991                 cell.hide();
59992                 
59993                 (function(){ // complex but required for focus issues in safari, ie and opera
59994                     ed.row = row;
59995                     ed.col = col;
59996                     ed.record = r;
59997                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59998                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59999                     this.activeEditor = ed;
60000                     var v = r.data[field];
60001                     ed.startEdit(this.view.getCell(row, col), v);
60002                     // combo's with 'displayField and name set
60003                     if (ed.field.displayField && ed.field.name) {
60004                         ed.field.el.dom.value = r.data[ed.field.name];
60005                     }
60006                     
60007                     
60008                 }).defer(50, this);
60009             }
60010         }
60011     },
60012         
60013     /**
60014      * Stops any active editing
60015      */
60016     stopEditing : function(){
60017         if(this.activeEditor){
60018             this.activeEditor.completeEdit();
60019         }
60020         this.activeEditor = null;
60021     },
60022         
60023          /**
60024      * Called to get grid's drag proxy text, by default returns this.ddText.
60025      * @return {String}
60026      */
60027     getDragDropText : function(){
60028         var count = this.selModel.getSelectedCell() ? 1 : 0;
60029         return String.format(this.ddText, count, count == 1 ? '' : 's');
60030     }
60031         
60032 });/*
60033  * Based on:
60034  * Ext JS Library 1.1.1
60035  * Copyright(c) 2006-2007, Ext JS, LLC.
60036  *
60037  * Originally Released Under LGPL - original licence link has changed is not relivant.
60038  *
60039  * Fork - LGPL
60040  * <script type="text/javascript">
60041  */
60042
60043 // private - not really -- you end up using it !
60044 // This is a support class used internally by the Grid components
60045
60046 /**
60047  * @class Roo.grid.GridEditor
60048  * @extends Roo.Editor
60049  * Class for creating and editable grid elements.
60050  * @param {Object} config any settings (must include field)
60051  */
60052 Roo.grid.GridEditor = function(field, config){
60053     if (!config && field.field) {
60054         config = field;
60055         field = Roo.factory(config.field, Roo.form);
60056     }
60057     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60058     field.monitorTab = false;
60059 };
60060
60061 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60062     
60063     /**
60064      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60065      */
60066     
60067     alignment: "tl-tl",
60068     autoSize: "width",
60069     hideEl : false,
60070     cls: "x-small-editor x-grid-editor",
60071     shim:false,
60072     shadow:"frame"
60073 });/*
60074  * Based on:
60075  * Ext JS Library 1.1.1
60076  * Copyright(c) 2006-2007, Ext JS, LLC.
60077  *
60078  * Originally Released Under LGPL - original licence link has changed is not relivant.
60079  *
60080  * Fork - LGPL
60081  * <script type="text/javascript">
60082  */
60083   
60084
60085   
60086 Roo.grid.PropertyRecord = Roo.data.Record.create([
60087     {name:'name',type:'string'},  'value'
60088 ]);
60089
60090
60091 Roo.grid.PropertyStore = function(grid, source){
60092     this.grid = grid;
60093     this.store = new Roo.data.Store({
60094         recordType : Roo.grid.PropertyRecord
60095     });
60096     this.store.on('update', this.onUpdate,  this);
60097     if(source){
60098         this.setSource(source);
60099     }
60100     Roo.grid.PropertyStore.superclass.constructor.call(this);
60101 };
60102
60103
60104
60105 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60106     setSource : function(o){
60107         this.source = o;
60108         this.store.removeAll();
60109         var data = [];
60110         for(var k in o){
60111             if(this.isEditableValue(o[k])){
60112                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60113             }
60114         }
60115         this.store.loadRecords({records: data}, {}, true);
60116     },
60117
60118     onUpdate : function(ds, record, type){
60119         if(type == Roo.data.Record.EDIT){
60120             var v = record.data['value'];
60121             var oldValue = record.modified['value'];
60122             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60123                 this.source[record.id] = v;
60124                 record.commit();
60125                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60126             }else{
60127                 record.reject();
60128             }
60129         }
60130     },
60131
60132     getProperty : function(row){
60133        return this.store.getAt(row);
60134     },
60135
60136     isEditableValue: function(val){
60137         if(val && val instanceof Date){
60138             return true;
60139         }else if(typeof val == 'object' || typeof val == 'function'){
60140             return false;
60141         }
60142         return true;
60143     },
60144
60145     setValue : function(prop, value){
60146         this.source[prop] = value;
60147         this.store.getById(prop).set('value', value);
60148     },
60149
60150     getSource : function(){
60151         return this.source;
60152     }
60153 });
60154
60155 Roo.grid.PropertyColumnModel = function(grid, store){
60156     this.grid = grid;
60157     var g = Roo.grid;
60158     g.PropertyColumnModel.superclass.constructor.call(this, [
60159         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60160         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60161     ]);
60162     this.store = store;
60163     this.bselect = Roo.DomHelper.append(document.body, {
60164         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60165             {tag: 'option', value: 'true', html: 'true'},
60166             {tag: 'option', value: 'false', html: 'false'}
60167         ]
60168     });
60169     Roo.id(this.bselect);
60170     var f = Roo.form;
60171     this.editors = {
60172         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60173         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60174         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60175         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60176         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60177     };
60178     this.renderCellDelegate = this.renderCell.createDelegate(this);
60179     this.renderPropDelegate = this.renderProp.createDelegate(this);
60180 };
60181
60182 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60183     
60184     
60185     nameText : 'Name',
60186     valueText : 'Value',
60187     
60188     dateFormat : 'm/j/Y',
60189     
60190     
60191     renderDate : function(dateVal){
60192         return dateVal.dateFormat(this.dateFormat);
60193     },
60194
60195     renderBool : function(bVal){
60196         return bVal ? 'true' : 'false';
60197     },
60198
60199     isCellEditable : function(colIndex, rowIndex){
60200         return colIndex == 1;
60201     },
60202
60203     getRenderer : function(col){
60204         return col == 1 ?
60205             this.renderCellDelegate : this.renderPropDelegate;
60206     },
60207
60208     renderProp : function(v){
60209         return this.getPropertyName(v);
60210     },
60211
60212     renderCell : function(val){
60213         var rv = val;
60214         if(val instanceof Date){
60215             rv = this.renderDate(val);
60216         }else if(typeof val == 'boolean'){
60217             rv = this.renderBool(val);
60218         }
60219         return Roo.util.Format.htmlEncode(rv);
60220     },
60221
60222     getPropertyName : function(name){
60223         var pn = this.grid.propertyNames;
60224         return pn && pn[name] ? pn[name] : name;
60225     },
60226
60227     getCellEditor : function(colIndex, rowIndex){
60228         var p = this.store.getProperty(rowIndex);
60229         var n = p.data['name'], val = p.data['value'];
60230         
60231         if(typeof(this.grid.customEditors[n]) == 'string'){
60232             return this.editors[this.grid.customEditors[n]];
60233         }
60234         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60235             return this.grid.customEditors[n];
60236         }
60237         if(val instanceof Date){
60238             return this.editors['date'];
60239         }else if(typeof val == 'number'){
60240             return this.editors['number'];
60241         }else if(typeof val == 'boolean'){
60242             return this.editors['boolean'];
60243         }else{
60244             return this.editors['string'];
60245         }
60246     }
60247 });
60248
60249 /**
60250  * @class Roo.grid.PropertyGrid
60251  * @extends Roo.grid.EditorGrid
60252  * This class represents the  interface of a component based property grid control.
60253  * <br><br>Usage:<pre><code>
60254  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60255       
60256  });
60257  // set any options
60258  grid.render();
60259  * </code></pre>
60260   
60261  * @constructor
60262  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60263  * The container MUST have some type of size defined for the grid to fill. The container will be
60264  * automatically set to position relative if it isn't already.
60265  * @param {Object} config A config object that sets properties on this grid.
60266  */
60267 Roo.grid.PropertyGrid = function(container, config){
60268     config = config || {};
60269     var store = new Roo.grid.PropertyStore(this);
60270     this.store = store;
60271     var cm = new Roo.grid.PropertyColumnModel(this, store);
60272     store.store.sort('name', 'ASC');
60273     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60274         ds: store.store,
60275         cm: cm,
60276         enableColLock:false,
60277         enableColumnMove:false,
60278         stripeRows:false,
60279         trackMouseOver: false,
60280         clicksToEdit:1
60281     }, config));
60282     this.getGridEl().addClass('x-props-grid');
60283     this.lastEditRow = null;
60284     this.on('columnresize', this.onColumnResize, this);
60285     this.addEvents({
60286          /**
60287              * @event beforepropertychange
60288              * Fires before a property changes (return false to stop?)
60289              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60290              * @param {String} id Record Id
60291              * @param {String} newval New Value
60292          * @param {String} oldval Old Value
60293              */
60294         "beforepropertychange": true,
60295         /**
60296              * @event propertychange
60297              * Fires after a property changes
60298              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60299              * @param {String} id Record Id
60300              * @param {String} newval New Value
60301          * @param {String} oldval Old Value
60302              */
60303         "propertychange": true
60304     });
60305     this.customEditors = this.customEditors || {};
60306 };
60307 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60308     
60309      /**
60310      * @cfg {Object} customEditors map of colnames=> custom editors.
60311      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60312      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60313      * false disables editing of the field.
60314          */
60315     
60316       /**
60317      * @cfg {Object} propertyNames map of property Names to their displayed value
60318          */
60319     
60320     render : function(){
60321         Roo.grid.PropertyGrid.superclass.render.call(this);
60322         this.autoSize.defer(100, this);
60323     },
60324
60325     autoSize : function(){
60326         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60327         if(this.view){
60328             this.view.fitColumns();
60329         }
60330     },
60331
60332     onColumnResize : function(){
60333         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60334         this.autoSize();
60335     },
60336     /**
60337      * Sets the data for the Grid
60338      * accepts a Key => Value object of all the elements avaiable.
60339      * @param {Object} data  to appear in grid.
60340      */
60341     setSource : function(source){
60342         this.store.setSource(source);
60343         //this.autoSize();
60344     },
60345     /**
60346      * Gets all the data from the grid.
60347      * @return {Object} data  data stored in grid
60348      */
60349     getSource : function(){
60350         return this.store.getSource();
60351     }
60352 });/*
60353   
60354  * Licence LGPL
60355  
60356  */
60357  
60358 /**
60359  * @class Roo.grid.Calendar
60360  * @extends Roo.util.Grid
60361  * This class extends the Grid to provide a calendar widget
60362  * <br><br>Usage:<pre><code>
60363  var grid = new Roo.grid.Calendar("my-container-id", {
60364      ds: myDataStore,
60365      cm: myColModel,
60366      selModel: mySelectionModel,
60367      autoSizeColumns: true,
60368      monitorWindowResize: false,
60369      trackMouseOver: true
60370      eventstore : real data store..
60371  });
60372  // set any options
60373  grid.render();
60374   
60375   * @constructor
60376  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60377  * The container MUST have some type of size defined for the grid to fill. The container will be
60378  * automatically set to position relative if it isn't already.
60379  * @param {Object} config A config object that sets properties on this grid.
60380  */
60381 Roo.grid.Calendar = function(container, config){
60382         // initialize the container
60383         this.container = Roo.get(container);
60384         this.container.update("");
60385         this.container.setStyle("overflow", "hidden");
60386     this.container.addClass('x-grid-container');
60387
60388     this.id = this.container.id;
60389
60390     Roo.apply(this, config);
60391     // check and correct shorthanded configs
60392     
60393     var rows = [];
60394     var d =1;
60395     for (var r = 0;r < 6;r++) {
60396         
60397         rows[r]=[];
60398         for (var c =0;c < 7;c++) {
60399             rows[r][c]= '';
60400         }
60401     }
60402     if (this.eventStore) {
60403         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60404         this.eventStore.on('load',this.onLoad, this);
60405         this.eventStore.on('beforeload',this.clearEvents, this);
60406          
60407     }
60408     
60409     this.dataSource = new Roo.data.Store({
60410             proxy: new Roo.data.MemoryProxy(rows),
60411             reader: new Roo.data.ArrayReader({}, [
60412                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60413     });
60414
60415     this.dataSource.load();
60416     this.ds = this.dataSource;
60417     this.ds.xmodule = this.xmodule || false;
60418     
60419     
60420     var cellRender = function(v,x,r)
60421     {
60422         return String.format(
60423             '<div class="fc-day  fc-widget-content"><div>' +
60424                 '<div class="fc-event-container"></div>' +
60425                 '<div class="fc-day-number">{0}</div>'+
60426                 
60427                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60428             '</div></div>', v);
60429     
60430     }
60431     
60432     
60433     this.colModel = new Roo.grid.ColumnModel( [
60434         {
60435             xtype: 'ColumnModel',
60436             xns: Roo.grid,
60437             dataIndex : 'weekday0',
60438             header : 'Sunday',
60439             renderer : cellRender
60440         },
60441         {
60442             xtype: 'ColumnModel',
60443             xns: Roo.grid,
60444             dataIndex : 'weekday1',
60445             header : 'Monday',
60446             renderer : cellRender
60447         },
60448         {
60449             xtype: 'ColumnModel',
60450             xns: Roo.grid,
60451             dataIndex : 'weekday2',
60452             header : 'Tuesday',
60453             renderer : cellRender
60454         },
60455         {
60456             xtype: 'ColumnModel',
60457             xns: Roo.grid,
60458             dataIndex : 'weekday3',
60459             header : 'Wednesday',
60460             renderer : cellRender
60461         },
60462         {
60463             xtype: 'ColumnModel',
60464             xns: Roo.grid,
60465             dataIndex : 'weekday4',
60466             header : 'Thursday',
60467             renderer : cellRender
60468         },
60469         {
60470             xtype: 'ColumnModel',
60471             xns: Roo.grid,
60472             dataIndex : 'weekday5',
60473             header : 'Friday',
60474             renderer : cellRender
60475         },
60476         {
60477             xtype: 'ColumnModel',
60478             xns: Roo.grid,
60479             dataIndex : 'weekday6',
60480             header : 'Saturday',
60481             renderer : cellRender
60482         }
60483     ]);
60484     this.cm = this.colModel;
60485     this.cm.xmodule = this.xmodule || false;
60486  
60487         
60488           
60489     //this.selModel = new Roo.grid.CellSelectionModel();
60490     //this.sm = this.selModel;
60491     //this.selModel.init(this);
60492     
60493     
60494     if(this.width){
60495         this.container.setWidth(this.width);
60496     }
60497
60498     if(this.height){
60499         this.container.setHeight(this.height);
60500     }
60501     /** @private */
60502         this.addEvents({
60503         // raw events
60504         /**
60505          * @event click
60506          * The raw click event for the entire grid.
60507          * @param {Roo.EventObject} e
60508          */
60509         "click" : true,
60510         /**
60511          * @event dblclick
60512          * The raw dblclick event for the entire grid.
60513          * @param {Roo.EventObject} e
60514          */
60515         "dblclick" : true,
60516         /**
60517          * @event contextmenu
60518          * The raw contextmenu event for the entire grid.
60519          * @param {Roo.EventObject} e
60520          */
60521         "contextmenu" : true,
60522         /**
60523          * @event mousedown
60524          * The raw mousedown event for the entire grid.
60525          * @param {Roo.EventObject} e
60526          */
60527         "mousedown" : true,
60528         /**
60529          * @event mouseup
60530          * The raw mouseup event for the entire grid.
60531          * @param {Roo.EventObject} e
60532          */
60533         "mouseup" : true,
60534         /**
60535          * @event mouseover
60536          * The raw mouseover event for the entire grid.
60537          * @param {Roo.EventObject} e
60538          */
60539         "mouseover" : true,
60540         /**
60541          * @event mouseout
60542          * The raw mouseout event for the entire grid.
60543          * @param {Roo.EventObject} e
60544          */
60545         "mouseout" : true,
60546         /**
60547          * @event keypress
60548          * The raw keypress event for the entire grid.
60549          * @param {Roo.EventObject} e
60550          */
60551         "keypress" : true,
60552         /**
60553          * @event keydown
60554          * The raw keydown event for the entire grid.
60555          * @param {Roo.EventObject} e
60556          */
60557         "keydown" : true,
60558
60559         // custom events
60560
60561         /**
60562          * @event cellclick
60563          * Fires when a cell is clicked
60564          * @param {Grid} this
60565          * @param {Number} rowIndex
60566          * @param {Number} columnIndex
60567          * @param {Roo.EventObject} e
60568          */
60569         "cellclick" : true,
60570         /**
60571          * @event celldblclick
60572          * Fires when a cell is double clicked
60573          * @param {Grid} this
60574          * @param {Number} rowIndex
60575          * @param {Number} columnIndex
60576          * @param {Roo.EventObject} e
60577          */
60578         "celldblclick" : true,
60579         /**
60580          * @event rowclick
60581          * Fires when a row is clicked
60582          * @param {Grid} this
60583          * @param {Number} rowIndex
60584          * @param {Roo.EventObject} e
60585          */
60586         "rowclick" : true,
60587         /**
60588          * @event rowdblclick
60589          * Fires when a row is double clicked
60590          * @param {Grid} this
60591          * @param {Number} rowIndex
60592          * @param {Roo.EventObject} e
60593          */
60594         "rowdblclick" : true,
60595         /**
60596          * @event headerclick
60597          * Fires when a header is clicked
60598          * @param {Grid} this
60599          * @param {Number} columnIndex
60600          * @param {Roo.EventObject} e
60601          */
60602         "headerclick" : true,
60603         /**
60604          * @event headerdblclick
60605          * Fires when a header cell is double clicked
60606          * @param {Grid} this
60607          * @param {Number} columnIndex
60608          * @param {Roo.EventObject} e
60609          */
60610         "headerdblclick" : true,
60611         /**
60612          * @event rowcontextmenu
60613          * Fires when a row is right clicked
60614          * @param {Grid} this
60615          * @param {Number} rowIndex
60616          * @param {Roo.EventObject} e
60617          */
60618         "rowcontextmenu" : true,
60619         /**
60620          * @event cellcontextmenu
60621          * Fires when a cell is right clicked
60622          * @param {Grid} this
60623          * @param {Number} rowIndex
60624          * @param {Number} cellIndex
60625          * @param {Roo.EventObject} e
60626          */
60627          "cellcontextmenu" : true,
60628         /**
60629          * @event headercontextmenu
60630          * Fires when a header is right clicked
60631          * @param {Grid} this
60632          * @param {Number} columnIndex
60633          * @param {Roo.EventObject} e
60634          */
60635         "headercontextmenu" : true,
60636         /**
60637          * @event bodyscroll
60638          * Fires when the body element is scrolled
60639          * @param {Number} scrollLeft
60640          * @param {Number} scrollTop
60641          */
60642         "bodyscroll" : true,
60643         /**
60644          * @event columnresize
60645          * Fires when the user resizes a column
60646          * @param {Number} columnIndex
60647          * @param {Number} newSize
60648          */
60649         "columnresize" : true,
60650         /**
60651          * @event columnmove
60652          * Fires when the user moves a column
60653          * @param {Number} oldIndex
60654          * @param {Number} newIndex
60655          */
60656         "columnmove" : true,
60657         /**
60658          * @event startdrag
60659          * Fires when row(s) start being dragged
60660          * @param {Grid} this
60661          * @param {Roo.GridDD} dd The drag drop object
60662          * @param {event} e The raw browser event
60663          */
60664         "startdrag" : true,
60665         /**
60666          * @event enddrag
60667          * Fires when a drag operation is complete
60668          * @param {Grid} this
60669          * @param {Roo.GridDD} dd The drag drop object
60670          * @param {event} e The raw browser event
60671          */
60672         "enddrag" : true,
60673         /**
60674          * @event dragdrop
60675          * Fires when dragged row(s) are dropped on a valid DD target
60676          * @param {Grid} this
60677          * @param {Roo.GridDD} dd The drag drop object
60678          * @param {String} targetId The target drag drop object
60679          * @param {event} e The raw browser event
60680          */
60681         "dragdrop" : true,
60682         /**
60683          * @event dragover
60684          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60685          * @param {Grid} this
60686          * @param {Roo.GridDD} dd The drag drop object
60687          * @param {String} targetId The target drag drop object
60688          * @param {event} e The raw browser event
60689          */
60690         "dragover" : true,
60691         /**
60692          * @event dragenter
60693          *  Fires when the dragged row(s) first cross another DD target while being dragged
60694          * @param {Grid} this
60695          * @param {Roo.GridDD} dd The drag drop object
60696          * @param {String} targetId The target drag drop object
60697          * @param {event} e The raw browser event
60698          */
60699         "dragenter" : true,
60700         /**
60701          * @event dragout
60702          * Fires when the dragged row(s) leave another DD target while being dragged
60703          * @param {Grid} this
60704          * @param {Roo.GridDD} dd The drag drop object
60705          * @param {String} targetId The target drag drop object
60706          * @param {event} e The raw browser event
60707          */
60708         "dragout" : true,
60709         /**
60710          * @event rowclass
60711          * Fires when a row is rendered, so you can change add a style to it.
60712          * @param {GridView} gridview   The grid view
60713          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60714          */
60715         'rowclass' : true,
60716
60717         /**
60718          * @event render
60719          * Fires when the grid is rendered
60720          * @param {Grid} grid
60721          */
60722         'render' : true,
60723             /**
60724              * @event select
60725              * Fires when a date is selected
60726              * @param {DatePicker} this
60727              * @param {Date} date The selected date
60728              */
60729         'select': true,
60730         /**
60731              * @event monthchange
60732              * Fires when the displayed month changes 
60733              * @param {DatePicker} this
60734              * @param {Date} date The selected month
60735              */
60736         'monthchange': true,
60737         /**
60738              * @event evententer
60739              * Fires when mouse over an event
60740              * @param {Calendar} this
60741              * @param {event} Event
60742              */
60743         'evententer': true,
60744         /**
60745              * @event eventleave
60746              * Fires when the mouse leaves an
60747              * @param {Calendar} this
60748              * @param {event}
60749              */
60750         'eventleave': true,
60751         /**
60752              * @event eventclick
60753              * Fires when the mouse click an
60754              * @param {Calendar} this
60755              * @param {event}
60756              */
60757         'eventclick': true,
60758         /**
60759              * @event eventrender
60760              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60761              * @param {Calendar} this
60762              * @param {data} data to be modified
60763              */
60764         'eventrender': true
60765         
60766     });
60767
60768     Roo.grid.Grid.superclass.constructor.call(this);
60769     this.on('render', function() {
60770         this.view.el.addClass('x-grid-cal'); 
60771         
60772         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60773
60774     },this);
60775     
60776     if (!Roo.grid.Calendar.style) {
60777         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60778             
60779             
60780             '.x-grid-cal .x-grid-col' :  {
60781                 height: 'auto !important',
60782                 'vertical-align': 'top'
60783             },
60784             '.x-grid-cal  .fc-event-hori' : {
60785                 height: '14px'
60786             }
60787              
60788             
60789         }, Roo.id());
60790     }
60791
60792     
60793     
60794 };
60795 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60796     /**
60797      * @cfg {Store} eventStore The store that loads events.
60798      */
60799     eventStore : 25,
60800
60801      
60802     activeDate : false,
60803     startDay : 0,
60804     autoWidth : true,
60805     monitorWindowResize : false,
60806
60807     
60808     resizeColumns : function() {
60809         var col = (this.view.el.getWidth() / 7) - 3;
60810         // loop through cols, and setWidth
60811         for(var i =0 ; i < 7 ; i++){
60812             this.cm.setColumnWidth(i, col);
60813         }
60814     },
60815      setDate :function(date) {
60816         
60817         Roo.log('setDate?');
60818         
60819         this.resizeColumns();
60820         var vd = this.activeDate;
60821         this.activeDate = date;
60822 //        if(vd && this.el){
60823 //            var t = date.getTime();
60824 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60825 //                Roo.log('using add remove');
60826 //                
60827 //                this.fireEvent('monthchange', this, date);
60828 //                
60829 //                this.cells.removeClass("fc-state-highlight");
60830 //                this.cells.each(function(c){
60831 //                   if(c.dateValue == t){
60832 //                       c.addClass("fc-state-highlight");
60833 //                       setTimeout(function(){
60834 //                            try{c.dom.firstChild.focus();}catch(e){}
60835 //                       }, 50);
60836 //                       return false;
60837 //                   }
60838 //                   return true;
60839 //                });
60840 //                return;
60841 //            }
60842 //        }
60843         
60844         var days = date.getDaysInMonth();
60845         
60846         var firstOfMonth = date.getFirstDateOfMonth();
60847         var startingPos = firstOfMonth.getDay()-this.startDay;
60848         
60849         if(startingPos < this.startDay){
60850             startingPos += 7;
60851         }
60852         
60853         var pm = date.add(Date.MONTH, -1);
60854         var prevStart = pm.getDaysInMonth()-startingPos;
60855 //        
60856         
60857         
60858         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60859         
60860         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60861         //this.cells.addClassOnOver('fc-state-hover');
60862         
60863         var cells = this.cells.elements;
60864         var textEls = this.textNodes;
60865         
60866         //Roo.each(cells, function(cell){
60867         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60868         //});
60869         
60870         days += startingPos;
60871
60872         // convert everything to numbers so it's fast
60873         var day = 86400000;
60874         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60875         //Roo.log(d);
60876         //Roo.log(pm);
60877         //Roo.log(prevStart);
60878         
60879         var today = new Date().clearTime().getTime();
60880         var sel = date.clearTime().getTime();
60881         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60882         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60883         var ddMatch = this.disabledDatesRE;
60884         var ddText = this.disabledDatesText;
60885         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60886         var ddaysText = this.disabledDaysText;
60887         var format = this.format;
60888         
60889         var setCellClass = function(cal, cell){
60890             
60891             //Roo.log('set Cell Class');
60892             cell.title = "";
60893             var t = d.getTime();
60894             
60895             //Roo.log(d);
60896             
60897             
60898             cell.dateValue = t;
60899             if(t == today){
60900                 cell.className += " fc-today";
60901                 cell.className += " fc-state-highlight";
60902                 cell.title = cal.todayText;
60903             }
60904             if(t == sel){
60905                 // disable highlight in other month..
60906                 cell.className += " fc-state-highlight";
60907                 
60908             }
60909             // disabling
60910             if(t < min) {
60911                 //cell.className = " fc-state-disabled";
60912                 cell.title = cal.minText;
60913                 return;
60914             }
60915             if(t > max) {
60916                 //cell.className = " fc-state-disabled";
60917                 cell.title = cal.maxText;
60918                 return;
60919             }
60920             if(ddays){
60921                 if(ddays.indexOf(d.getDay()) != -1){
60922                     // cell.title = ddaysText;
60923                    // cell.className = " fc-state-disabled";
60924                 }
60925             }
60926             if(ddMatch && format){
60927                 var fvalue = d.dateFormat(format);
60928                 if(ddMatch.test(fvalue)){
60929                     cell.title = ddText.replace("%0", fvalue);
60930                    cell.className = " fc-state-disabled";
60931                 }
60932             }
60933             
60934             if (!cell.initialClassName) {
60935                 cell.initialClassName = cell.dom.className;
60936             }
60937             
60938             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60939         };
60940
60941         var i = 0;
60942         
60943         for(; i < startingPos; i++) {
60944             cells[i].dayName =  (++prevStart);
60945             Roo.log(textEls[i]);
60946             d.setDate(d.getDate()+1);
60947             
60948             //cells[i].className = "fc-past fc-other-month";
60949             setCellClass(this, cells[i]);
60950         }
60951         
60952         var intDay = 0;
60953         
60954         for(; i < days; i++){
60955             intDay = i - startingPos + 1;
60956             cells[i].dayName =  (intDay);
60957             d.setDate(d.getDate()+1);
60958             
60959             cells[i].className = ''; // "x-date-active";
60960             setCellClass(this, cells[i]);
60961         }
60962         var extraDays = 0;
60963         
60964         for(; i < 42; i++) {
60965             //textEls[i].innerHTML = (++extraDays);
60966             
60967             d.setDate(d.getDate()+1);
60968             cells[i].dayName = (++extraDays);
60969             cells[i].className = "fc-future fc-other-month";
60970             setCellClass(this, cells[i]);
60971         }
60972         
60973         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60974         
60975         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60976         
60977         // this will cause all the cells to mis
60978         var rows= [];
60979         var i =0;
60980         for (var r = 0;r < 6;r++) {
60981             for (var c =0;c < 7;c++) {
60982                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60983             }    
60984         }
60985         
60986         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60987         for(i=0;i<cells.length;i++) {
60988             
60989             this.cells.elements[i].dayName = cells[i].dayName ;
60990             this.cells.elements[i].className = cells[i].className;
60991             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60992             this.cells.elements[i].title = cells[i].title ;
60993             this.cells.elements[i].dateValue = cells[i].dateValue ;
60994         }
60995         
60996         
60997         
60998         
60999         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61000         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61001         
61002         ////if(totalRows != 6){
61003             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61004            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61005        // }
61006         
61007         this.fireEvent('monthchange', this, date);
61008         
61009         
61010     },
61011  /**
61012      * Returns the grid's SelectionModel.
61013      * @return {SelectionModel}
61014      */
61015     getSelectionModel : function(){
61016         if(!this.selModel){
61017             this.selModel = new Roo.grid.CellSelectionModel();
61018         }
61019         return this.selModel;
61020     },
61021
61022     load: function() {
61023         this.eventStore.load()
61024         
61025         
61026         
61027     },
61028     
61029     findCell : function(dt) {
61030         dt = dt.clearTime().getTime();
61031         var ret = false;
61032         this.cells.each(function(c){
61033             //Roo.log("check " +c.dateValue + '?=' + dt);
61034             if(c.dateValue == dt){
61035                 ret = c;
61036                 return false;
61037             }
61038             return true;
61039         });
61040         
61041         return ret;
61042     },
61043     
61044     findCells : function(rec) {
61045         var s = rec.data.start_dt.clone().clearTime().getTime();
61046        // Roo.log(s);
61047         var e= rec.data.end_dt.clone().clearTime().getTime();
61048        // Roo.log(e);
61049         var ret = [];
61050         this.cells.each(function(c){
61051              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61052             
61053             if(c.dateValue > e){
61054                 return ;
61055             }
61056             if(c.dateValue < s){
61057                 return ;
61058             }
61059             ret.push(c);
61060         });
61061         
61062         return ret;    
61063     },
61064     
61065     findBestRow: function(cells)
61066     {
61067         var ret = 0;
61068         
61069         for (var i =0 ; i < cells.length;i++) {
61070             ret  = Math.max(cells[i].rows || 0,ret);
61071         }
61072         return ret;
61073         
61074     },
61075     
61076     
61077     addItem : function(rec)
61078     {
61079         // look for vertical location slot in
61080         var cells = this.findCells(rec);
61081         
61082         rec.row = this.findBestRow(cells);
61083         
61084         // work out the location.
61085         
61086         var crow = false;
61087         var rows = [];
61088         for(var i =0; i < cells.length; i++) {
61089             if (!crow) {
61090                 crow = {
61091                     start : cells[i],
61092                     end :  cells[i]
61093                 };
61094                 continue;
61095             }
61096             if (crow.start.getY() == cells[i].getY()) {
61097                 // on same row.
61098                 crow.end = cells[i];
61099                 continue;
61100             }
61101             // different row.
61102             rows.push(crow);
61103             crow = {
61104                 start: cells[i],
61105                 end : cells[i]
61106             };
61107             
61108         }
61109         
61110         rows.push(crow);
61111         rec.els = [];
61112         rec.rows = rows;
61113         rec.cells = cells;
61114         for (var i = 0; i < cells.length;i++) {
61115             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61116             
61117         }
61118         
61119         
61120     },
61121     
61122     clearEvents: function() {
61123         
61124         if (!this.eventStore.getCount()) {
61125             return;
61126         }
61127         // reset number of rows in cells.
61128         Roo.each(this.cells.elements, function(c){
61129             c.rows = 0;
61130         });
61131         
61132         this.eventStore.each(function(e) {
61133             this.clearEvent(e);
61134         },this);
61135         
61136     },
61137     
61138     clearEvent : function(ev)
61139     {
61140         if (ev.els) {
61141             Roo.each(ev.els, function(el) {
61142                 el.un('mouseenter' ,this.onEventEnter, this);
61143                 el.un('mouseleave' ,this.onEventLeave, this);
61144                 el.remove();
61145             },this);
61146             ev.els = [];
61147         }
61148     },
61149     
61150     
61151     renderEvent : function(ev,ctr) {
61152         if (!ctr) {
61153              ctr = this.view.el.select('.fc-event-container',true).first();
61154         }
61155         
61156          
61157         this.clearEvent(ev);
61158             //code
61159        
61160         
61161         
61162         ev.els = [];
61163         var cells = ev.cells;
61164         var rows = ev.rows;
61165         this.fireEvent('eventrender', this, ev);
61166         
61167         for(var i =0; i < rows.length; i++) {
61168             
61169             cls = '';
61170             if (i == 0) {
61171                 cls += ' fc-event-start';
61172             }
61173             if ((i+1) == rows.length) {
61174                 cls += ' fc-event-end';
61175             }
61176             
61177             //Roo.log(ev.data);
61178             // how many rows should it span..
61179             var cg = this.eventTmpl.append(ctr,Roo.apply({
61180                 fccls : cls
61181                 
61182             }, ev.data) , true);
61183             
61184             
61185             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61186             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61187             cg.on('click', this.onEventClick, this, ev);
61188             
61189             ev.els.push(cg);
61190             
61191             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61192             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61193             //Roo.log(cg);
61194              
61195             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61196             cg.setWidth(ebox.right - sbox.x -2);
61197         }
61198     },
61199     
61200     renderEvents: function()
61201     {   
61202         // first make sure there is enough space..
61203         
61204         if (!this.eventTmpl) {
61205             this.eventTmpl = new Roo.Template(
61206                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61207                     '<div class="fc-event-inner">' +
61208                         '<span class="fc-event-time">{time}</span>' +
61209                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61210                     '</div>' +
61211                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61212                 '</div>'
61213             );
61214                 
61215         }
61216                
61217         
61218         
61219         this.cells.each(function(c) {
61220             //Roo.log(c.select('.fc-day-content div',true).first());
61221             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61222         });
61223         
61224         var ctr = this.view.el.select('.fc-event-container',true).first();
61225         
61226         var cls;
61227         this.eventStore.each(function(ev){
61228             
61229             this.renderEvent(ev);
61230              
61231              
61232         }, this);
61233         this.view.layout();
61234         
61235     },
61236     
61237     onEventEnter: function (e, el,event,d) {
61238         this.fireEvent('evententer', this, el, event);
61239     },
61240     
61241     onEventLeave: function (e, el,event,d) {
61242         this.fireEvent('eventleave', this, el, event);
61243     },
61244     
61245     onEventClick: function (e, el,event,d) {
61246         this.fireEvent('eventclick', this, el, event);
61247     },
61248     
61249     onMonthChange: function () {
61250         this.store.load();
61251     },
61252     
61253     onLoad: function () {
61254         
61255         //Roo.log('calendar onload');
61256 //         
61257         if(this.eventStore.getCount() > 0){
61258             
61259            
61260             
61261             this.eventStore.each(function(d){
61262                 
61263                 
61264                 // FIXME..
61265                 var add =   d.data;
61266                 if (typeof(add.end_dt) == 'undefined')  {
61267                     Roo.log("Missing End time in calendar data: ");
61268                     Roo.log(d);
61269                     return;
61270                 }
61271                 if (typeof(add.start_dt) == 'undefined')  {
61272                     Roo.log("Missing Start time in calendar data: ");
61273                     Roo.log(d);
61274                     return;
61275                 }
61276                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61277                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61278                 add.id = add.id || d.id;
61279                 add.title = add.title || '??';
61280                 
61281                 this.addItem(d);
61282                 
61283              
61284             },this);
61285         }
61286         
61287         this.renderEvents();
61288     }
61289     
61290
61291 });
61292 /*
61293  grid : {
61294                 xtype: 'Grid',
61295                 xns: Roo.grid,
61296                 listeners : {
61297                     render : function ()
61298                     {
61299                         _this.grid = this;
61300                         
61301                         if (!this.view.el.hasClass('course-timesheet')) {
61302                             this.view.el.addClass('course-timesheet');
61303                         }
61304                         if (this.tsStyle) {
61305                             this.ds.load({});
61306                             return; 
61307                         }
61308                         Roo.log('width');
61309                         Roo.log(_this.grid.view.el.getWidth());
61310                         
61311                         
61312                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61313                             '.course-timesheet .x-grid-row' : {
61314                                 height: '80px'
61315                             },
61316                             '.x-grid-row td' : {
61317                                 'vertical-align' : 0
61318                             },
61319                             '.course-edit-link' : {
61320                                 'color' : 'blue',
61321                                 'text-overflow' : 'ellipsis',
61322                                 'overflow' : 'hidden',
61323                                 'white-space' : 'nowrap',
61324                                 'cursor' : 'pointer'
61325                             },
61326                             '.sub-link' : {
61327                                 'color' : 'green'
61328                             },
61329                             '.de-act-sup-link' : {
61330                                 'color' : 'purple',
61331                                 'text-decoration' : 'line-through'
61332                             },
61333                             '.de-act-link' : {
61334                                 'color' : 'red',
61335                                 'text-decoration' : 'line-through'
61336                             },
61337                             '.course-timesheet .course-highlight' : {
61338                                 'border-top-style': 'dashed !important',
61339                                 'border-bottom-bottom': 'dashed !important'
61340                             },
61341                             '.course-timesheet .course-item' : {
61342                                 'font-family'   : 'tahoma, arial, helvetica',
61343                                 'font-size'     : '11px',
61344                                 'overflow'      : 'hidden',
61345                                 'padding-left'  : '10px',
61346                                 'padding-right' : '10px',
61347                                 'padding-top' : '10px' 
61348                             }
61349                             
61350                         }, Roo.id());
61351                                 this.ds.load({});
61352                     }
61353                 },
61354                 autoWidth : true,
61355                 monitorWindowResize : false,
61356                 cellrenderer : function(v,x,r)
61357                 {
61358                     return v;
61359                 },
61360                 sm : {
61361                     xtype: 'CellSelectionModel',
61362                     xns: Roo.grid
61363                 },
61364                 dataSource : {
61365                     xtype: 'Store',
61366                     xns: Roo.data,
61367                     listeners : {
61368                         beforeload : function (_self, options)
61369                         {
61370                             options.params = options.params || {};
61371                             options.params._month = _this.monthField.getValue();
61372                             options.params.limit = 9999;
61373                             options.params['sort'] = 'when_dt';    
61374                             options.params['dir'] = 'ASC';    
61375                             this.proxy.loadResponse = this.loadResponse;
61376                             Roo.log("load?");
61377                             //this.addColumns();
61378                         },
61379                         load : function (_self, records, options)
61380                         {
61381                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61382                                 // if you click on the translation.. you can edit it...
61383                                 var el = Roo.get(this);
61384                                 var id = el.dom.getAttribute('data-id');
61385                                 var d = el.dom.getAttribute('data-date');
61386                                 var t = el.dom.getAttribute('data-time');
61387                                 //var id = this.child('span').dom.textContent;
61388                                 
61389                                 //Roo.log(this);
61390                                 Pman.Dialog.CourseCalendar.show({
61391                                     id : id,
61392                                     when_d : d,
61393                                     when_t : t,
61394                                     productitem_active : id ? 1 : 0
61395                                 }, function() {
61396                                     _this.grid.ds.load({});
61397                                 });
61398                            
61399                            });
61400                            
61401                            _this.panel.fireEvent('resize', [ '', '' ]);
61402                         }
61403                     },
61404                     loadResponse : function(o, success, response){
61405                             // this is overridden on before load..
61406                             
61407                             Roo.log("our code?");       
61408                             //Roo.log(success);
61409                             //Roo.log(response)
61410                             delete this.activeRequest;
61411                             if(!success){
61412                                 this.fireEvent("loadexception", this, o, response);
61413                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61414                                 return;
61415                             }
61416                             var result;
61417                             try {
61418                                 result = o.reader.read(response);
61419                             }catch(e){
61420                                 Roo.log("load exception?");
61421                                 this.fireEvent("loadexception", this, o, response, e);
61422                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61423                                 return;
61424                             }
61425                             Roo.log("ready...");        
61426                             // loop through result.records;
61427                             // and set this.tdate[date] = [] << array of records..
61428                             _this.tdata  = {};
61429                             Roo.each(result.records, function(r){
61430                                 //Roo.log(r.data);
61431                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61432                                     _this.tdata[r.data.when_dt.format('j')] = [];
61433                                 }
61434                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61435                             });
61436                             
61437                             //Roo.log(_this.tdata);
61438                             
61439                             result.records = [];
61440                             result.totalRecords = 6;
61441                     
61442                             // let's generate some duumy records for the rows.
61443                             //var st = _this.dateField.getValue();
61444                             
61445                             // work out monday..
61446                             //st = st.add(Date.DAY, -1 * st.format('w'));
61447                             
61448                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61449                             
61450                             var firstOfMonth = date.getFirstDayOfMonth();
61451                             var days = date.getDaysInMonth();
61452                             var d = 1;
61453                             var firstAdded = false;
61454                             for (var i = 0; i < result.totalRecords ; i++) {
61455                                 //var d= st.add(Date.DAY, i);
61456                                 var row = {};
61457                                 var added = 0;
61458                                 for(var w = 0 ; w < 7 ; w++){
61459                                     if(!firstAdded && firstOfMonth != w){
61460                                         continue;
61461                                     }
61462                                     if(d > days){
61463                                         continue;
61464                                     }
61465                                     firstAdded = true;
61466                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61467                                     row['weekday'+w] = String.format(
61468                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61469                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61470                                                     d,
61471                                                     date.format('Y-m-')+dd
61472                                                 );
61473                                     added++;
61474                                     if(typeof(_this.tdata[d]) != 'undefined'){
61475                                         Roo.each(_this.tdata[d], function(r){
61476                                             var is_sub = '';
61477                                             var deactive = '';
61478                                             var id = r.id;
61479                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61480                                             if(r.parent_id*1>0){
61481                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61482                                                 id = r.parent_id;
61483                                             }
61484                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61485                                                 deactive = 'de-act-link';
61486                                             }
61487                                             
61488                                             row['weekday'+w] += String.format(
61489                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61490                                                     id, //0
61491                                                     r.product_id_name, //1
61492                                                     r.when_dt.format('h:ia'), //2
61493                                                     is_sub, //3
61494                                                     deactive, //4
61495                                                     desc // 5
61496                                             );
61497                                         });
61498                                     }
61499                                     d++;
61500                                 }
61501                                 
61502                                 // only do this if something added..
61503                                 if(added > 0){ 
61504                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61505                                 }
61506                                 
61507                                 
61508                                 // push it twice. (second one with an hour..
61509                                 
61510                             }
61511                             //Roo.log(result);
61512                             this.fireEvent("load", this, o, o.request.arg);
61513                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61514                         },
61515                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61516                     proxy : {
61517                         xtype: 'HttpProxy',
61518                         xns: Roo.data,
61519                         method : 'GET',
61520                         url : baseURL + '/Roo/Shop_course.php'
61521                     },
61522                     reader : {
61523                         xtype: 'JsonReader',
61524                         xns: Roo.data,
61525                         id : 'id',
61526                         fields : [
61527                             {
61528                                 'name': 'id',
61529                                 'type': 'int'
61530                             },
61531                             {
61532                                 'name': 'when_dt',
61533                                 'type': 'string'
61534                             },
61535                             {
61536                                 'name': 'end_dt',
61537                                 'type': 'string'
61538                             },
61539                             {
61540                                 'name': 'parent_id',
61541                                 'type': 'int'
61542                             },
61543                             {
61544                                 'name': 'product_id',
61545                                 'type': 'int'
61546                             },
61547                             {
61548                                 'name': 'productitem_id',
61549                                 'type': 'int'
61550                             },
61551                             {
61552                                 'name': 'guid',
61553                                 'type': 'int'
61554                             }
61555                         ]
61556                     }
61557                 },
61558                 toolbar : {
61559                     xtype: 'Toolbar',
61560                     xns: Roo,
61561                     items : [
61562                         {
61563                             xtype: 'Button',
61564                             xns: Roo.Toolbar,
61565                             listeners : {
61566                                 click : function (_self, e)
61567                                 {
61568                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61569                                     sd.setMonth(sd.getMonth()-1);
61570                                     _this.monthField.setValue(sd.format('Y-m-d'));
61571                                     _this.grid.ds.load({});
61572                                 }
61573                             },
61574                             text : "Back"
61575                         },
61576                         {
61577                             xtype: 'Separator',
61578                             xns: Roo.Toolbar
61579                         },
61580                         {
61581                             xtype: 'MonthField',
61582                             xns: Roo.form,
61583                             listeners : {
61584                                 render : function (_self)
61585                                 {
61586                                     _this.monthField = _self;
61587                                    // _this.monthField.set  today
61588                                 },
61589                                 select : function (combo, date)
61590                                 {
61591                                     _this.grid.ds.load({});
61592                                 }
61593                             },
61594                             value : (function() { return new Date(); })()
61595                         },
61596                         {
61597                             xtype: 'Separator',
61598                             xns: Roo.Toolbar
61599                         },
61600                         {
61601                             xtype: 'TextItem',
61602                             xns: Roo.Toolbar,
61603                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61604                         },
61605                         {
61606                             xtype: 'Fill',
61607                             xns: Roo.Toolbar
61608                         },
61609                         {
61610                             xtype: 'Button',
61611                             xns: Roo.Toolbar,
61612                             listeners : {
61613                                 click : function (_self, e)
61614                                 {
61615                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61616                                     sd.setMonth(sd.getMonth()+1);
61617                                     _this.monthField.setValue(sd.format('Y-m-d'));
61618                                     _this.grid.ds.load({});
61619                                 }
61620                             },
61621                             text : "Next"
61622                         }
61623                     ]
61624                 },
61625                  
61626             }
61627         };
61628         
61629         *//*
61630  * Based on:
61631  * Ext JS Library 1.1.1
61632  * Copyright(c) 2006-2007, Ext JS, LLC.
61633  *
61634  * Originally Released Under LGPL - original licence link has changed is not relivant.
61635  *
61636  * Fork - LGPL
61637  * <script type="text/javascript">
61638  */
61639  
61640 /**
61641  * @class Roo.LoadMask
61642  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61643  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61644  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61645  * element's UpdateManager load indicator and will be destroyed after the initial load.
61646  * @constructor
61647  * Create a new LoadMask
61648  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61649  * @param {Object} config The config object
61650  */
61651 Roo.LoadMask = function(el, config){
61652     this.el = Roo.get(el);
61653     Roo.apply(this, config);
61654     if(this.store){
61655         this.store.on('beforeload', this.onBeforeLoad, this);
61656         this.store.on('load', this.onLoad, this);
61657         this.store.on('loadexception', this.onLoadException, this);
61658         this.removeMask = false;
61659     }else{
61660         var um = this.el.getUpdateManager();
61661         um.showLoadIndicator = false; // disable the default indicator
61662         um.on('beforeupdate', this.onBeforeLoad, this);
61663         um.on('update', this.onLoad, this);
61664         um.on('failure', this.onLoad, this);
61665         this.removeMask = true;
61666     }
61667 };
61668
61669 Roo.LoadMask.prototype = {
61670     /**
61671      * @cfg {Boolean} removeMask
61672      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61673      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61674      */
61675     removeMask : false,
61676     /**
61677      * @cfg {String} msg
61678      * The text to display in a centered loading message box (defaults to 'Loading...')
61679      */
61680     msg : 'Loading...',
61681     /**
61682      * @cfg {String} msgCls
61683      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61684      */
61685     msgCls : 'x-mask-loading',
61686
61687     /**
61688      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61689      * @type Boolean
61690      */
61691     disabled: false,
61692
61693     /**
61694      * Disables the mask to prevent it from being displayed
61695      */
61696     disable : function(){
61697        this.disabled = true;
61698     },
61699
61700     /**
61701      * Enables the mask so that it can be displayed
61702      */
61703     enable : function(){
61704         this.disabled = false;
61705     },
61706     
61707     onLoadException : function()
61708     {
61709         Roo.log(arguments);
61710         
61711         if (typeof(arguments[3]) != 'undefined') {
61712             Roo.MessageBox.alert("Error loading",arguments[3]);
61713         } 
61714         /*
61715         try {
61716             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61717                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61718             }   
61719         } catch(e) {
61720             
61721         }
61722         */
61723     
61724         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61725     },
61726     // private
61727     onLoad : function()
61728     {
61729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61730     },
61731
61732     // private
61733     onBeforeLoad : function(){
61734         if(!this.disabled){
61735             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61736         }
61737     },
61738
61739     // private
61740     destroy : function(){
61741         if(this.store){
61742             this.store.un('beforeload', this.onBeforeLoad, this);
61743             this.store.un('load', this.onLoad, this);
61744             this.store.un('loadexception', this.onLoadException, this);
61745         }else{
61746             var um = this.el.getUpdateManager();
61747             um.un('beforeupdate', this.onBeforeLoad, this);
61748             um.un('update', this.onLoad, this);
61749             um.un('failure', this.onLoad, this);
61750         }
61751     }
61752 };/*
61753  * Based on:
61754  * Ext JS Library 1.1.1
61755  * Copyright(c) 2006-2007, Ext JS, LLC.
61756  *
61757  * Originally Released Under LGPL - original licence link has changed is not relivant.
61758  *
61759  * Fork - LGPL
61760  * <script type="text/javascript">
61761  */
61762
61763
61764 /**
61765  * @class Roo.XTemplate
61766  * @extends Roo.Template
61767  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61768 <pre><code>
61769 var t = new Roo.XTemplate(
61770         '&lt;select name="{name}"&gt;',
61771                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61772         '&lt;/select&gt;'
61773 );
61774  
61775 // then append, applying the master template values
61776  </code></pre>
61777  *
61778  * Supported features:
61779  *
61780  *  Tags:
61781
61782 <pre><code>
61783       {a_variable} - output encoded.
61784       {a_variable.format:("Y-m-d")} - call a method on the variable
61785       {a_variable:raw} - unencoded output
61786       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61787       {a_variable:this.method_on_template(...)} - call a method on the template object.
61788  
61789 </code></pre>
61790  *  The tpl tag:
61791 <pre><code>
61792         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61793         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61794         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61795         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61796   
61797         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61798         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61799 </code></pre>
61800  *      
61801  */
61802 Roo.XTemplate = function()
61803 {
61804     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61805     if (this.html) {
61806         this.compile();
61807     }
61808 };
61809
61810
61811 Roo.extend(Roo.XTemplate, Roo.Template, {
61812
61813     /**
61814      * The various sub templates
61815      */
61816     tpls : false,
61817     /**
61818      *
61819      * basic tag replacing syntax
61820      * WORD:WORD()
61821      *
61822      * // you can fake an object call by doing this
61823      *  x.t:(test,tesT) 
61824      * 
61825      */
61826     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61827
61828     /**
61829      * compile the template
61830      *
61831      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61832      *
61833      */
61834     compile: function()
61835     {
61836         var s = this.html;
61837      
61838         s = ['<tpl>', s, '</tpl>'].join('');
61839     
61840         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61841             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61842             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61843             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61844             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61845             m,
61846             id     = 0,
61847             tpls   = [];
61848     
61849         while(true == !!(m = s.match(re))){
61850             var forMatch   = m[0].match(nameRe),
61851                 ifMatch   = m[0].match(ifRe),
61852                 execMatch   = m[0].match(execRe),
61853                 namedMatch   = m[0].match(namedRe),
61854                 
61855                 exp  = null, 
61856                 fn   = null,
61857                 exec = null,
61858                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61859                 
61860             if (ifMatch) {
61861                 // if - puts fn into test..
61862                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61863                 if(exp){
61864                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61865                 }
61866             }
61867             
61868             if (execMatch) {
61869                 // exec - calls a function... returns empty if true is  returned.
61870                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61871                 if(exp){
61872                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61873                 }
61874             }
61875             
61876             
61877             if (name) {
61878                 // for = 
61879                 switch(name){
61880                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61881                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61882                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61883                 }
61884             }
61885             var uid = namedMatch ? namedMatch[1] : id;
61886             
61887             
61888             tpls.push({
61889                 id:     namedMatch ? namedMatch[1] : id,
61890                 target: name,
61891                 exec:   exec,
61892                 test:   fn,
61893                 body:   m[1] || ''
61894             });
61895             if (namedMatch) {
61896                 s = s.replace(m[0], '');
61897             } else { 
61898                 s = s.replace(m[0], '{xtpl'+ id + '}');
61899             }
61900             ++id;
61901         }
61902         this.tpls = [];
61903         for(var i = tpls.length-1; i >= 0; --i){
61904             this.compileTpl(tpls[i]);
61905             this.tpls[tpls[i].id] = tpls[i];
61906         }
61907         this.master = tpls[tpls.length-1];
61908         return this;
61909     },
61910     /**
61911      * same as applyTemplate, except it's done to one of the subTemplates
61912      * when using named templates, you can do:
61913      *
61914      * var str = pl.applySubTemplate('your-name', values);
61915      *
61916      * 
61917      * @param {Number} id of the template
61918      * @param {Object} values to apply to template
61919      * @param {Object} parent (normaly the instance of this object)
61920      */
61921     applySubTemplate : function(id, values, parent)
61922     {
61923         
61924         
61925         var t = this.tpls[id];
61926         
61927         
61928         try { 
61929             if(t.test && !t.test.call(this, values, parent)){
61930                 return '';
61931             }
61932         } catch(e) {
61933             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61934             Roo.log(e.toString());
61935             Roo.log(t.test);
61936             return ''
61937         }
61938         try { 
61939             
61940             if(t.exec && t.exec.call(this, values, parent)){
61941                 return '';
61942             }
61943         } catch(e) {
61944             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61945             Roo.log(e.toString());
61946             Roo.log(t.exec);
61947             return ''
61948         }
61949         try {
61950             var vs = t.target ? t.target.call(this, values, parent) : values;
61951             parent = t.target ? values : parent;
61952             if(t.target && vs instanceof Array){
61953                 var buf = [];
61954                 for(var i = 0, len = vs.length; i < len; i++){
61955                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61956                 }
61957                 return buf.join('');
61958             }
61959             return t.compiled.call(this, vs, parent);
61960         } catch (e) {
61961             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61962             Roo.log(e.toString());
61963             Roo.log(t.compiled);
61964             return '';
61965         }
61966     },
61967
61968     compileTpl : function(tpl)
61969     {
61970         var fm = Roo.util.Format;
61971         var useF = this.disableFormats !== true;
61972         var sep = Roo.isGecko ? "+" : ",";
61973         var undef = function(str) {
61974             Roo.log("Property not found :"  + str);
61975             return '';
61976         };
61977         
61978         var fn = function(m, name, format, args)
61979         {
61980             //Roo.log(arguments);
61981             args = args ? args.replace(/\\'/g,"'") : args;
61982             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61983             if (typeof(format) == 'undefined') {
61984                 format= 'htmlEncode';
61985             }
61986             if (format == 'raw' ) {
61987                 format = false;
61988             }
61989             
61990             if(name.substr(0, 4) == 'xtpl'){
61991                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61992             }
61993             
61994             // build an array of options to determine if value is undefined..
61995             
61996             // basically get 'xxxx.yyyy' then do
61997             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61998             //    (function () { Roo.log("Property not found"); return ''; })() :
61999             //    ......
62000             
62001             var udef_ar = [];
62002             var lookfor = '';
62003             Roo.each(name.split('.'), function(st) {
62004                 lookfor += (lookfor.length ? '.': '') + st;
62005                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62006             });
62007             
62008             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62009             
62010             
62011             if(format && useF){
62012                 
62013                 args = args ? ',' + args : "";
62014                  
62015                 if(format.substr(0, 5) != "this."){
62016                     format = "fm." + format + '(';
62017                 }else{
62018                     format = 'this.call("'+ format.substr(5) + '", ';
62019                     args = ", values";
62020                 }
62021                 
62022                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62023             }
62024              
62025             if (args.length) {
62026                 // called with xxyx.yuu:(test,test)
62027                 // change to ()
62028                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62029             }
62030             // raw.. - :raw modifier..
62031             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62032             
62033         };
62034         var body;
62035         // branched to use + in gecko and [].join() in others
62036         if(Roo.isGecko){
62037             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62038                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62039                     "';};};";
62040         }else{
62041             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62042             body.push(tpl.body.replace(/(\r\n|\n)/g,
62043                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62044             body.push("'].join('');};};");
62045             body = body.join('');
62046         }
62047         
62048         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62049        
62050         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62051         eval(body);
62052         
62053         return this;
62054     },
62055
62056     applyTemplate : function(values){
62057         return this.master.compiled.call(this, values, {});
62058         //var s = this.subs;
62059     },
62060
62061     apply : function(){
62062         return this.applyTemplate.apply(this, arguments);
62063     }
62064
62065  });
62066
62067 Roo.XTemplate.from = function(el){
62068     el = Roo.getDom(el);
62069     return new Roo.XTemplate(el.value || el.innerHTML);
62070 };